<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[  Single Page Applications  - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[  Single Page Applications  - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 28 Jul 2026 03:52:21 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/single-page-applications/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use React Router to Build Single Page Applications ]]>
                </title>
                <description>
                    <![CDATA[ Single Page Applications (SPAs) have revolutionized web development. They offer a more dynamic and fluid user experience compared to traditional multi-page applications. Traditional web apps require full-page reloads for almost every click the user m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-react-router-to-build-single-page-applications/</link>
                <guid isPermaLink="false">66ba29324d935175898a7067</guid>
                
                    <category>
                        <![CDATA[ react router ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Timothy Olanrewaju ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jul 2024 14:42:16 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Using-React-Router-to-build-SPAs--Twitter-Post-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Single Page Applications (SPAs) have revolutionized web development. They offer a more dynamic and fluid user experience compared to traditional multi-page applications.</p>
<p>Traditional web apps require full-page reloads for almost every click the user makes. SPAs, on the other hand, load a single HTML page and update the page contents dynamically as users interact with the application. This dynamism mimics the feel of desktop applications and results in better responsive interactions.</p>
<h3 id="heading-before-moving-any-further">Before moving any further:</h3>
<p>To follow along with what I'll discuss in this article, you should have some basic knowledge of React and how to set up a React project. If you already do, let's crack on.</p>
<h2 id="heading-what-are-react-router-and-react-router-dom">What are React Router and React Router DOM?</h2>
<p>React router is a powerful library that manages navigation and routing in React applications. The React Router DOM is used specifically for web applications and has few DOM-specific APIs.</p>
<p>As we dive into the world of React Router DOM, we'll explore its core concepts while demonstrating their implementation in a React application. Our focus will be on building a simple navigation system with links to different components, illustrating how to configure routes, handle route matching, and implement navigation. </p>
<p>By the end of this article, you'll have a solid understanding of how to use React Router DOM to create dynamic and seamless navigation experiences in your single-page applications.</p>
<h2 id="heading-how-to-install-react-router">How to Install React Router</h2>
<p>As I explained above, React-router-DOM is used exclusively for integrating routing functionalities into web applications. So, in order to use it in your React app, you need to install the <code>react-router-dom</code> package by running this command in your React app terminal:</p>
<pre><code class="lang-react">npm install react-router-dom
</code></pre>
<p>After successfully installing it, you can now start routing in your React project. </p>
<h2 id="heading-core-concepts-in-react-router-dom">Core Concepts in React Router DOM</h2>
<h3 id="heading-browserrouter">BrowserRouter</h3>
<p>BrowserRouter a parent component that houses all the route components. All routes that you use in an application must be declared within the <code>BrowserRouter</code>. Most importantly, it stores the current location in the browser's address bar using URLs which comes in handy during navigation.</p>
<p>To use the BrowserRouter, you'll need to import it from the <code>react-router-dom</code> in your App.jsx file.</p>
<pre><code class="lang-react">import { BrowserRouter } from "react-router-dom";

function App() {

  return (
    &lt;BrowserRouter&gt;

    &lt;/BrowserRouter&gt;
  );
}

export default App;
</code></pre>
<p>The <code>BrowserRouter</code> has a <code>basename</code> attribute used to set base URL for all routes in an application. It's important if your app is hosted in a subdirectory on a domain. </p>
<pre><code>&lt;BrowserRouter basename=<span class="hljs-string">"/shop"</span>&gt;

&lt;/BrowserRouter&gt;
</code></pre><p>Adding <code>/shop</code> as a basename will make sure that all route paths are relative to <code>/shop</code>. </p>
<h3 id="heading-routes">Routes</h3>
<p>This component is a direct replacement for <code>switch</code> which was used in the former versions of React Router. It also acts like a parent and renders the first matching child route, which ensures that the correct component is displayed based on the current URL.</p>
<p>To declare routes, import <code>routes</code> from <code>react-router-dom</code> and position it within the <code>BrowserRouter</code> component.</p>
<pre><code class="lang-react">import { BrowserRouter, routes } from "react-router-dom";

function App() {

  return (
    &lt;BrowserRouter&gt;
        &lt;Routes&gt;

        &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App;
</code></pre>
<h3 id="heading-route">Route</h3>
<p><code>Route</code> a child component that consists of two attributes: <strong>path</strong> and <strong>element</strong>. A <strong>path</strong> can be any specified path name while the <strong>element</strong> attribute is the component that should be rendered. A route renders a specific component when the path specified matches a URL.</p>
<p>An application can have as many <code>route</code>s as it needs, and they must all be declared inside the <code>Routes</code> component. Assuming we have a <code>&lt;Home\&gt;</code> and <code>&lt;Pricing\&gt;</code> component, we will have to import the <code>Route</code> component and position it within the <code>Routes</code>.</p>
<pre><code class="lang-react">import { BrowserRouter, Routes, Route } from "react-router-dom";

//ALL COMPONENTS IMPORTS COMES HERE

function App() {

  return (
    &lt;BrowserRouter&gt;
        &lt;Routes&gt;
            &lt;Route path="/" element={&lt;Home/&gt;}/&gt;
                &lt;Route path="pricing" element={&lt;Pricing/&gt;}/&gt;
        &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App;
</code></pre>
<h3 id="heading-undeclared-routes">Undeclared Routes</h3>
<p>There is a way to handle routes that do not exist in your application, just like an Error 404 page. To do this, create another component bearing a Not Found message and the <code>route</code> added.</p>
<p>Set the path name to <code>*</code> and pass the component as the <strong>element.</strong></p>
<pre><code><span class="hljs-keyword">import</span> { BrowserRouter, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-comment">//ALL COMPONENTS IMPORTS COMES HERE</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"pricing"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Pricing</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">PageNotFound</span>/&gt;</span>}/&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre><h3 id="heading-nested-routes">Nested Routes</h3>
<p>In some cases, routes can have children or sub-routes.</p>
<pre><code><span class="hljs-keyword">import</span> { BrowserRouter, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-comment">//ALL COMPONENTS IMPORTS COMES HERE</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"pricing"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Pricing</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"categories"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Categories</span>/&gt;</span>}&gt;
                    <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"male"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Male</span>/&gt;</span>}/&gt;
                        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"female"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Female</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">PageNotFound</span>/&gt;</span>}/&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre><p>On navigating to the nested elements, the URL on the browser will be reading something like <code>/categories/male</code> and <code>/categories/female</code>.</p>
<h3 id="heading-link">Link</h3>
<p>This acts like an anchor <code>href</code> attribute. It has a <strong>to</strong> attribute which specifies where the <code>Link</code> will take the user after a click. Usually, it is the path to a component's page that is passed to the <strong>to</strong> attribute. </p>
<p>Links are typically put in a Navbar component, so we will put two Links that point to the components path in our already declared Routes.</p>
<pre><code class="lang-react">import { Link } from "react-router-dom";
export default function PageNav() {
  return (
  &lt;&gt;
        &lt;Link to="/"&gt;Home&lt;/Link&gt;
        &lt;Link to="pricing"&gt;Pricing&lt;/Link&gt;
  &lt;/&gt;
  );
}
</code></pre>
<p><strong>NB:</strong> If you are practicing along while reading this article, it is important to note that the <code>PageNav</code> component created here should be situated in your <strong>App.jsx</strong> and specifically just after the opening <code>BrowserRouter</code> tag before the Routes. This is to ensure the <code>PageNav</code> always stays at the top like a navigation menu despite routing through different components.</p>
<pre><code><span class="hljs-keyword">import</span> { BrowserRouter, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-comment">//ALL COMPONENTS IMPORTS COMES HERE</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">PageNav</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"pricing"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Pricing</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"categories"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Categories</span>/&gt;</span>}&gt;
                    <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"male"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Male</span>/&gt;</span>}/&gt;
                        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"female"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Female</span>/&gt;</span>}/&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">PageNotFound</span>/&gt;</span>}/&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre><h3 id="heading-navlink">NavLink</h3>
<p>NavLink performs the same function as <code>Link</code> and has a <strong>to</strong> attribute as well. But it's different as it has a class attribute. The class attributes are <code>active</code>, <code>isPending</code>, and <code>isTransitioning</code>. This makes it more versatile than <code>Link</code> and you can use it to conditionally add styles during user interactions.</p>
<pre><code><span class="hljs-keyword">import</span> { NavLink } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PageNav</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"pricing"</span>&gt;</span>Pricing<span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
  }
</code></pre><h3 id="heading-outlet">Outlet</h3>
<p>Having child elements inside a parent route element means there is a layer of abstraction in rendering the child routes' UI. This is where the <code>Outlet</code> component comes into play. You add it to the parent route – in our example, it would be the <code>Categories</code> component.</p>
<pre><code class="lang-react">import { NavLink, Outlet } from "react-router-dom";
export default function Categories() {
  return (
 &lt;&gt;
          &lt;NavLink to="men"&gt;
            Men
          &lt;/NavLink&gt;
          &lt;NavLink to="women"&gt;
            Women
          &lt;/NavLink&gt;
      &lt;Outlet /&gt;
&lt;/&gt;
  );
}
</code></pre>
<p>This allows for the rendering of the child routes UI in the nested route.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/nested-routes.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing children nested routes in Categories route</em></p>
<h3 id="heading-usenavigate-hook"><code>useNavigate</code> Hook</h3>
<p>This hook returns a function that enables programmatic navigation between routes. </p>
<p>There are several ways to use the navigate function in your application. First, we need to import the <code>useNavigate</code> hook and initialize it as <strong>navigate</strong>.</p>
<pre><code><span class="hljs-keyword">import</span> { useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Homepage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> navigate = useNavigate();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is the Homepage<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>


    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre><p>We can use navigate in the following ways in our application:</p>
<ul>
<li>Attaching it to a button via the <code>onClick</code> prop with the intended path to be navigated to, passed to the navigate function.</li>
</ul>
<pre><code class="lang-react"> &lt;button onClick={() =&gt; navigate("/categories")}&gt;Go to Categories&lt;/button&gt;
</code></pre>
<ul>
<li>Using it with a <code>Link</code> component.</li>
</ul>
<pre><code class="lang-react">&lt;Link to={navigate("/categories")}&gt;Go to Categories&lt;/Link&gt;
</code></pre>
<ul>
<li>Using a number instead of the component path in the <strong>navigate</strong> function. The number should specify the number of navigations backward in the history stack where you would like to go.</li>
</ul>
<pre><code class="lang-react">&lt;Link to={navigate(-1)}&gt;Go one step backwards&lt;/Link&gt;
</code></pre>
<h3 id="heading-useparams-hook"><code>useParams</code> Hook</h3>
<p>Returns an object of the dynamic <code>params</code> gotten from the current URL matched by the Route's path. The parent routes pass all <code>params</code> to their child routes. </p>
<p>The example below shows an <code>OrderPage</code> component that will be rendered for every <code>customer</code> with their unique <code>id</code>. When the URL matches <code>/customer/123</code>, <code>:id</code> will be <code>123</code>.</p>
<pre><code class="lang-react">import { useParams } from "react-router-dom";

function App() {
  const {id} = useParams()
  return (
    &lt;BrowserRouter&gt;
        &lt;Routes&gt;
        &lt;Route path="customer"&gt;
            &lt;Route path=":id" element={&lt;OrderPage/&gt;}/&gt;
           &lt;/Route&gt;
        &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App;
</code></pre>
<h3 id="heading-final-result">Final Result</h3>
<p>At this point, we've fully implemented React Router in our small navigation project. This is what it looks like in full flow:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/Untitled-design.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For more detailed information and concepts about React router, you can visit the <a target="_blank" href="https://reactrouter.com/">Official React Router documentation</a> site.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we've explored the concepts and implementation of Client Side Routing (CSR) in a React web application. React Router, through its web-centric library React-Router-DOM, enables CSR, allowing apps to update the URL with a click of a link without making a server request for a new document.</p>
<p>This functionality enhances the user experience by providing faster navigation and a more seamless interaction within the application. By leveraging CSR, developers can build more efficient and responsive single-page applications (SPAs), ultimately improving performance and user satisfaction.</p>
<p>If you enjoyed reading this article, you could <a target="_blank" href="https://buymeacoffee.com/timothyolanrewaju">Buy me a Coffee</a>.</p>
<p>Want to see more of these? Connect with me on <a target="_blank" href="https://www.linkedin.com/in/timothy-olanrewaju750/">LinkedIn</a>.</p>
<p>See you on the next one!</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Your First Microsoft 365 Application in 10 minutes ]]>
                </title>
                <description>
                    <![CDATA[ By Waldek Mastykarz If you're a web developer and work with an organization that uses Microsoft 365, or you build applications that you sell to customers, I've got great news for you: you can use your existing skills to build applications that integr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-microsoft-365-application-in-10-minutes/</link>
                <guid isPermaLink="false">66d46171a3a4f04fb2dd2e6d</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microsoft ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 04 Oct 2022 17:32:26 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/banner.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Waldek Mastykarz</p>
<p>If you're a web developer and work with an organization that uses Microsoft 365, or you build applications that you sell to customers, I've got great news for you: you can use your existing skills to build applications that integrate with Microsoft 365.</p>
<h2 id="heading-whats-microsoft-365">What's Microsoft 365?</h2>
<p><a target="_blank" href="https://www.microsoft.com/microsoft-365">Microsoft 365</a> is a set of applications, like Microsoft Teams, Outlook, Word, or SharePoint, that organizations use for work. Every day, millions of people use Microsoft 365 to chat, meet, send emails, create documents, and more. </p>
<p>But Microsoft 365 isn't just a set of apps. It's also a platform that enables developers, like yourself, to build applications. These apps can tap into data and insights stored on Microsoft 365 and bring them into people's workflows.</p>
<h2 id="heading-you-can-build-your-first-microsoft-365-app-in-under-10-minutes">You Can Build your First Microsoft 365 App in Under 10 minutes</h2>
<p>Following is a short tutorial that'll show you how to build a simple single-page app that integrates with Microsoft 365. The app will let users sign in with their Microsoft 365 account, and see their profile information.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/microsoft-365-app-user-profile-information-1.png" alt="Browser window showing a web page with user profile information" width="600" height="400" loading="lazy">
<em>Microsoft 365 application</em></p>
<p>It doesn't seem like much, but while building this simple app, you'll learn the basics of building Microsoft 365 apps.</p>
<h3 id="heading-what-youll-need">What you'll need</h3>
<ul>
<li><a target="_blank" href="https://nodejs.org">Node.js LTS</a> (at the time of writing this article, it's 16.17.1)</li>
<li>a Microsoft 365 developer tenant. You can get one for free from <a target="_blank" href="https://developer.microsoft.com/microsoft-365/dev-program">Microsoft 365 developer program</a>, and it'll give you access to all Microsoft 365 APIs you need to build your apps, along with some demo data to get started</li>
<li>10 minutes of your time</li>
</ul>
<p>Ready? Let's go!</p>
<h2 id="heading-register-your-application-on-the-microsoft-cloud">Register your Application on the Microsoft Cloud</h2>
<p>Create a folder for your application, where you'll store all app files. Open a terminal and change the working directory to that folder.</p>
<p>In the terminal, run the following command:</p>
<pre><code class="lang-bash">npx -p @pnp/cli-microsoft365 -- m365 login --authType browser
</code></pre>
<p>In your web browser, sign in with your newly created Microsoft 365 developer account:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-30-at-12.12.08.png" alt="Microsoft 365 login screen" width="600" height="400" loading="lazy">
<em>Sign into your Microsoft 365 developer account</em></p>
<p>Next, back in the terminal, run the following command:</p>
<pre><code class="lang-bash">appId=$(npx -p @pnp/cli-microsoft365 -- m365 aad app add --name <span class="hljs-string">"Hello world"</span> --multitenant --redirectUris <span class="hljs-string">"http://localhost,http://localhost/index.html"</span> --platform spa --query <span class="hljs-string">"appId"</span> -o text)
</code></pre>
<p>With these two commands, you've used the <a target="_blank" href="https://aka.ms/cli-m365">CLI for Microsoft 365</a> to sign in to Microsoft 365 and register your new app on the Microsoft Cloud. </p>
<p>Every app that integrates with Microsoft 365 must be registered and provide information such as the application's name and type (platform). For single-page apps, you also need to specify the application's URL, which is used to ensure that users are signing in to the right app.</p>
<p>Next, write the ID of the newly created app to a file that you'll reference in your app. In the terminal, run:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"const appId = '<span class="hljs-variable">$appId</span>';"</span> &gt; env.js
</code></pre>
<h2 id="heading-create-your-app">Create your App</h2>
<p>Open your app folder in your code editor. Create a new file named <code>index.html</code> and paste the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  // TODO #1: add libraries
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello Microsoft 365<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"auth"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">pre</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-comment">// TODO #2: add app code</span>
  </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This code is a simple HTML page with two placeholders: one for the libraries that you'll use to build your app, and another for the app's code. It also contains a <code>div</code> where you'll show the login/logout button and a <code>pre</code> element where you'll show the user's profile information.</p>
<h2 id="heading-add-libraries">Add Libraries</h2>
<p>Replace the <code>TODO #1</code> comment with the following code:</p>
<pre><code class="lang-html">  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://alcdn.msauth.net/browser/2.28.3/js/msal-browser.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-js-sdk.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/@microsoft/microsoft-graph-client/lib/graph-client-msalBrowserAuthProvider.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./env.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>To build this app you'll use a few libraries:</p>
<ul>
<li><a target="_blank" href="https://learn.microsoft.com/azure/active-directory/develop/msal-overview?WT.mc_id=m365-78653-wmastyka">MSAL.js</a> which helps you handle signing users in with their Microsoft 365 account</li>
<li><a target="_blank" href="https://learn.microsoft.com/graph/sdks/sdk-installation#install-the-microsoft-graph-javascript-sdk">Microsoft Graph JavaScript SDK</a> which simplifies calling Microsoft Graph – the API to access data and insights on Microsoft 365</li>
<li>MSAL Browser Auth Provider which integrates MSAL.js with the Microsoft Graph JS SDK</li>
<li>the previously created <code>env.js</code> file with the ID of your app</li>
</ul>
<p>Using these libraries will help you build your app faster, and you won't have to worry about the details of how to sign users in, get an access token, or properly handle API errors.</p>
<h2 id="heading-sign-users-in-with-their-microsoft-365-account">Sign Users In with their Microsoft 365 Account</h2>
<p>Replace the <code>TODO #2</code> comment with the following code:</p>
<pre><code class="lang-javascript">    (<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-comment">// TODO #3: create MSAL client</span>

      <span class="hljs-comment">// TODO #5: handle login/logout</span>

      <span class="hljs-comment">// TODO #6: create Microsoft Graph client</span>

      <span class="hljs-comment">// TODO #7: get Microsoft 365 user profile</span>

      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">// TODO #4: render UI</span>
      }

      render();
    })();
</code></pre>
<p>This code is an immediately invoked function expression (IIFE) that encapsulates the app's code and runs when the app loads. It also contains several placeholders for the code that you'll write in the next steps.</p>
<p>Replace the <code>TODO #3</code> comment with the following code:</p>
<pre><code class="lang-javascript">      <span class="hljs-keyword">const</span> msalConfig = {
        <span class="hljs-attr">auth</span>: {
          <span class="hljs-attr">clientId</span>: appId
        }
      };
      <span class="hljs-keyword">const</span> msalClient = <span class="hljs-keyword">new</span> msal.PublicClientApplication(msalConfig);
</code></pre>
<p>In this fragment, you're creating a new configuration object for the MSAL.js library which includes a reference to the ID of the app you created earlier. </p>
<p>Next, you pass this object to the <code>PublicClientApplication</code> constructor to create a new instance of the MSAL client, which you'll use to sign users into your app with their Microsoft 365 account.</p>
<p>Right now, the app shows an empty page. Let's change that by replacing the <code>TODO #4</code> comment with the following code:</p>
<pre><code class="lang-javascript">        msalClient
          .handleRedirectPromise()
          .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> accounts = msalClient.getAllAccounts();

            <span class="hljs-keyword">if</span> (accounts.length === <span class="hljs-number">0</span>) {
              <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#auth'</span>).innerHTML = <span class="hljs-string">'&lt;button&gt;Login&lt;/button&gt;'</span>;
              <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#auth button'</span>).addEventListener(<span class="hljs-string">'click'</span>, login);
              <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'pre'</span>).innerHTML = <span class="hljs-string">''</span>;
            }
            <span class="hljs-keyword">else</span> {
              <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#auth'</span>).innerHTML = <span class="hljs-string">'&lt;button&gt;Logout&lt;/button&gt;'</span>;
              <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#auth button'</span>).addEventListener(<span class="hljs-string">'click'</span>, logout);
              <span class="hljs-comment">// TODO #8: show user profile</span>
            }
          });
</code></pre>
<p>When signing users in with their Microsoft 365 account, you'll redirect them to the Microsoft 365 sign-in page. When they sign in, they'll be redirected back to your app. </p>
<p>The <code>handleRedirectPromise</code> function will handle processing the information that Microsoft 365 sends to your app. When users come to your app and haven't signed in yet, the <code>handleRedirectPromise</code> function will resolve with a <code>null</code> response.</p>
<p>After handling the redirect, you check using MSAL if there are any users signed in to your app. If there are none (<code>accounts.length === 0</code> ), you show the login button. If there are users signed in, you show the logout button. Later, you'll add the code to show the user's profile information.</p>
<p>Both, the login and the logout button are missing their click handlers, so let's add them by replacing <code>TODO #5</code> with the following code:</p>
<pre><code class="lang-javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">login</span>(<span class="hljs-params">e</span>) </span>{
        e.preventDefault();
        msalClient.loginRedirect();
      }

      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logout</span>(<span class="hljs-params">e</span>) </span>{
        e.preventDefault();
        msalClient.logoutRedirect();
      }
</code></pre>
<p>In both cases, you're using MSAL to sign users in and out by redirecting them to the Microsoft 365 login/logout page.</p>
<p>At this stage, your app should let you sign in and out using your Microsoft 365 account. To verify that everything is working as intended, save your changes, and in the terminal run:</p>
<pre><code class="lang-bash">npx lite-server
</code></pre>
<p>In your web browser, navigate to <code>http://localhost:3000</code> and sign in to your app. You should see the following screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/microsoft-365-app-login.png" alt="Browser window with a web page showing a login button" width="600" height="400" loading="lazy">
<em>After signing into your app</em></p>
<p>When you click the login button, you will be asked to sign in with your Microsoft 365 account. Then, when you click the logout button, you will be signed out from Microsoft 365 and the app.</p>
<p>This concludes the first part of building the app, and you're ready to start retrieving data from Microsoft 365 using Microsoft Graph.</p>
<h2 id="heading-show-microsoft-365-user-profile">Show Microsoft 365 User Profile</h2>
<p>Now that your app supports signing in and out with Microsoft 365 accounts, the next step is to add the code to retrieve the user's profile information from Microsoft 365.</p>
<p>Replace the <code>TODO #6</code> comment with the following code:</p>
<pre><code class="lang-javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGraphClient</span>(<span class="hljs-params">account</span>) </span>{
        <span class="hljs-keyword">const</span> authProvider = <span class="hljs-keyword">new</span> MSGraphAuthCodeMSALBrowserAuthProvider.AuthCodeMSALBrowserAuthenticationProvider(msalClient, {
          account,
          <span class="hljs-attr">scopes</span>: [<span class="hljs-string">'user.read'</span>],
          <span class="hljs-attr">interactionType</span>: msal.InteractionType.Redirect,
        });

        <span class="hljs-keyword">return</span> MicrosoftGraph.Client.initWithMiddleware({ authProvider });
      }
</code></pre>
<p>This function takes as an argument the Microsoft 365 account that has been used to sign in to the app and uses it to create a Microsoft Graph client that you'll use to call Microsoft Graph APIs and get data from Microsoft 365.</p>
<p>Next, let's replace the <code>TODO #7</code> comment with the following code:</p>
<pre><code class="lang-javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadUserProfile</span>(<span class="hljs-params">graphClient</span>) </span>{
        graphClient
          .api(<span class="hljs-string">'/me'</span>)
          .get()
          .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'pre'</span>).innerHTML = <span class="hljs-built_in">JSON</span>.stringify(res, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>);
          });
      }
</code></pre>
<p>This function takes as an argument an instance of the Microsoft Graph client as returned by the Microsoft Graph JavaScript SDK and uses it to call the Microsoft Graph API. </p>
<p>In this case, you're calling the <code>/me</code> Microsoft Graph API which returns profile information for the currently signed-in user. The retrieved data is then displayed in the <code>pre</code> element on the page.</p>
<p>The final piece is to tie it all together and call both functions after the user signs in to the app. Replace the <code>TODO #8</code> comment with the following code:</p>
<pre><code class="lang-javascript">              <span class="hljs-keyword">const</span> graphClient = getGraphClient(accounts[<span class="hljs-number">0</span>]);
              loadUserProfile(graphClient);
</code></pre>
<p>That's it! When you save your changes and go back to the browser, you'll see it automatically refreshed in the background, and you're prompted to grant the app access to your profile information.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/microsoft-365-app-profile-permissions-prompt.png" alt="Dialog prompting the user to grant the application access to user profile information" width="600" height="400" loading="lazy">
<em>Setting permissions on the app</em></p>
<p>After your grant access, by clicking the <strong>Accept</strong> button, you'll see the profile information displayed in the <code>pre</code> element on the page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/microsoft-365-app-user-profile-information-2.png" alt="Browser window showing user profile information from Microsoft 365" width="600" height="400" loading="lazy">
<em>Profile information</em></p>
<p>Congratulations! You've just built your first app connected to Microsoft 365.</p>
<h2 id="heading-this-is-just-the-beginning">This is just the beginning</h2>
<p>You've just built your first app on Microsoft 365: a single-page app connected to Microsoft 365 that shows the Microsoft 365 profile of the current user. This app is a simple example to show you how to get started building apps on Microsoft 365.</p>
<p>There's a lot more <a target="_blank" href="https://learn.microsoft.com/graph/api/overview">data and insights stored in Microsoft 365</a> that your apps can access, and there are <a target="_blank" href="https://learn.microsoft.com/graph/overview">many types of apps</a> that you can build. The best part is that you can use the same techniques that you've learned in the last 10 minutes to build them.</p>
<p>You've just built a Microsoft 365 app using JavaScript, but you can build apps on Microsoft 365 using any programming language. One thing that all Microsoft 365 apps have got in common is that they bring in data and insights from Microsoft 365 helping people work together.</p>
<p>Curious to learn more? See what other <a target="_blank" href="https://adoption.microsoft.com/sample-solution-gallery/">developers</a> and <a target="_blank" href="https://adoption.microsoft.com/partner-solution-gallery/">Microsoft partners</a> are building on Microsoft 365. And if you want to learn further, check out the <a target="_blank" href="https://learn.microsoft.com/training/paths/m365-msgraph-fundamentals/">Microsoft Graph Fundamentals</a> learning path. And please, don't hesitate to reach out if you have any questions.  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Configure Metadata for a Single-Page Application ]]>
                </title>
                <description>
                    <![CDATA[ By Scott Gary Why Metadata Matters Metadata is an integral part of any modern web app, because it's inherently tethered to search engine optimization (SEO).  Search engines and their respective results page (SERPS) rely on metadata to properly index ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/configure-metadata-in-single-page-applications/</link>
                <guid isPermaLink="false">66d460f0bd438296f45cd3b6</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ metadata ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SEO ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 20 Sep 2022 17:20:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/meta-data-for-spa-seo.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Scott Gary</p>
<h2 id="heading-why-metadata-matters">Why Metadata Matters</h2>
<p>Metadata is an integral part of any modern web app, because it's inherently tethered to search engine optimization (SEO). </p>
<p>Search engines and their respective results page (SERPS) rely on metadata to properly index and display relative information for each site.</p>
<p>Also, meta tags are relied upon to properly display content from your site on a given social media platform, such as articles or items for sale.</p>
<p>For this reason, it's crucial to understand how metadata is configured in a modern web app.</p>
<p>The single page application (SPA) is a modern web app implementation that is incredibly popular. Most frameworks today utilize it in some way. Configuring metadata in today’s most popular SPA frameworks will be the focus of this tutorial.</p>
<h2 id="heading-the-single-page-application-and-metadata">The Single Page Application and Metadata</h2>
<p>The nature of SPAs make configuring metadata a less straightforward process than classic multiple page applications. I'm going to try to clarify this topic by describing the following key concepts:</p>
<ol>
<li>The structure of an SPA.</li>
<li>The problem with modifying metadata in an SPA.</li>
<li>Available metadata solutions using what are probably the three most popular SPA frameworks: React, Svelte, and Vue.</li>
</ol>
<p>You should have a basic understanding of HTML, metadata, and one of the three SPA frameworks to understand the concepts we’ll be going over. But, I’ll be keeping things beginner friendly, so don’t worry!</p>
<h2 id="heading-how-single-page-applications-work">How Single Page Applications Work</h2>
<p>Before diving in, you need a firm grasp of what constitutes an SPA. </p>
<p>As the name implies, a single page application literally consists of a single HTML page sent down from the server. This page is just an empty HTML shell, and will look something like this:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Home | Demystifying SPA Metadata<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"How to configure popular SPA             frameworks to maintain quality site metadata."</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./stylesheet.css"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>             /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/bundle.min.js"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>&gt;</span>                    <span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You might be wondering how an entire website is derived from this empty HTML shell.</p>
<p>This is possible because along with the HTML page will be extensive client-side JavaScript code that generates the content for each page. This code is included in the page via the </p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Full-Stack Single Page Application with Laravel 9, MySQL, Vue.js, Inertia, Jetstream and Docker ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific In this tutorial, you will learn how to build a single page application. I'll take you through the process step by step, using cutting edge technologies like Laravel 9, Jetstream, Vuejs, Inertiajs, MySQL, Tailwind CSS, and Docker. Le... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-full-stack-single-page-application-with-laravel-mysql-vue-and-docker/</link>
                <guid isPermaLink="false">66d45ee3052ad259f07e4ad4</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 16 May 2022 13:55:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/02/EP__3__LiveCoding.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>In this tutorial, you will learn how to build a single page application. I'll take you through the process step by step, using cutting edge technologies like Laravel 9, Jetstream, Vuejs, Inertiajs, MySQL, Tailwind CSS, and Docker.</p>
<p>Let's get started.</p>
<h2 id="heading-what-you-need-to-follow-this-guide">What you need to follow this guide:</h2>
<p>To follow along you will need: </p>
<ul>
<li>a computer</li>
<li>to know how to install software </li>
<li>a basic understanding of HTML, CSS, JavaScript, and PHP </li>
<li>knowledge of at least one JavaScript framework and an understanding of the MVC design pattern.</li>
</ul>
<p>This guide is organized into 10 chapters and is based off a live coding series that I record. The live coding series is completely unscripted, so there will be bugs and gotchas there that you won't find in this guide.</p>
<p>You can find the complete playlist at the end of this article.</p>
<p>Everything here should just work, but if it doesn't feel free to ask for help by joining my community on Slack. There you can share code snippets and chat with me directly. </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-tech-are-we-using">What Tech Are We Using?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-machine">How to Setup Your Machine</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-app-with-laravel-9-laravel-sail-jetstram-inertia-and-vue3">How to build the app with Laravel 9, Laravel Sail, Jetstram, Inertia and Vue3</a></li>
<li><a class="post-section-overview" href="#heading-how-to-refactor-the-admin-dashboard-and-create-new-admin-pages">How to Refactor the Admin Dashboard and Create New Admin Pages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-submit-forms-with-files">How to Submit Forms with Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-the-form-to-the-component">How to Add the Form to the Component</a></li>
<li><a class="post-section-overview" href="#heading-how-to-store-data">How to Store Data</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-operations">How to Update Operations</a></li>
<li><a class="post-section-overview" href="#heading-how-to-delete-a-resource">How to Delete a Resourse</a></li>
<li><a class="post-section-overview" href="#heading-wrapup-and-whats-next">Wrap up and what's next</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-tech-are-we-using">What Tech Are We Using?</h2>
<p>First, let's go over the different tools we'll be using in this project.</p>
<h3 id="heading-docker">Docker</h3>
<p>Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers. </p>
<p>To simplify this concept, Docker lets you package applications and dependencies in a container. </p>
<p>A containerized application allows you to have a flexible development environment so that you can run different applications without worrying about dependencies, their requirements, and conflicts between different versions. You can easily run applications that, for instance, require two different versions of PHP and MySQL. </p>
<p>Each team member can quickly reproduce the same environment of your application by simply running the same container's configuration.</p>
<p>If you want to learn more about Docker, its <a target="_blank" href="https://www.docker.com/">Documentation</a> is a great place to start.</p>
<p>Here's a <a target="_blank" href="https://www.freecodecamp.org/news/the-docker-handbook/">Handbook on Docker essentials</a>, as well, so you can practice your skills.</p>
<h3 id="heading-mysql">Mysql</h3>
<p>MySQL is an open-source relational database management system. You can use it to organize data into one or more tables with data that may be related to each other.</p>
<p>We need to store data somewhere and here is where MySQL comes into play.</p>
<p>Here are the <a target="_blank" href="https://www.mysql.com/">Docs</a> if you want to read up more. Here's a <a target="_blank" href="https://www.freecodecamp.org/news/learn-to-use-the-mysql-database/">full free course on MySQL</a> if you want to dive deeper.</p>
<h3 id="heading-laravel">Laravel</h3>
<p>Laravel is a free, open-source PHP web framework that helps you develop web applications following the model–view–controller architectural pattern.</p>
<p>Laravel is an amazing PHP framework that you can use to create bespoke web applications.</p>
<p>Here's the Laravel <a target="_blank" href="https://laravel.com/">Documentation</a> for more info, and here's a <a target="_blank" href="https://www.freecodecamp.org/news/laravel-full-course/">full project-based course</a> to help you learn Laravel.</p>
<h3 id="heading-laravel-sail">Laravel Sail</h3>
<p>Laravel Sail is a lightweight command-line interface for interacting with Laravel's default Docker development environment. </p>
<p>Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience.</p>
<p>Usually, creating a development environment to build such applications means you have to install software, languages, and frameworks on your local machine – and that is time-consuming. Thanks to Docker and Laravel Sail we will be up and running in no time!</p>
<p><strong>Laravel Sail is supported on macOS, Linux, and Windows <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">via WSL2</a>.</strong></p>
<p>Here's the <a target="_blank" href="https://laravel.com/docs/9.x/sail">Documentation</a> if you want to read up on it.</p>
<h3 id="heading-laravel-jetstream">Laravel Jetstream</h3>
<p>When building web applications, you likely want to let users register and log in to use your app. That is why we will use Jetstream.</p>
<p>Laravel Jetstream is a beautifully designed application starter kit for Laravel and provides the perfect starting point for your next Laravel application.</p>
<p>It uses Laravel Fortify to implement all the back end authentication logic.
Here are the <a target="_blank" href="https://jetstream.laravel.com/2.x/introduction.html">Docs</a>.</p>
<h3 id="heading-vuejs">Vuejs</h3>
<p>Vue.js is an open-source model–view–ViewModel front end JavaScript framework for building user interfaces and single-page applications.</p>
<p>Vue is a fantastic framework that you can use as a stand-alone to build single-page applications, but you can also use it with Laravel to build something amazing.</p>
<p>Here's the Vue <a target="_blank" href="https://vuejs.org/">Documentation</a> if you want to read up. And here's a <a target="_blank" href="https://www.freecodecamp.org/news/vue-3-full-course/">great Vue course</a> to get you started.</p>
<h3 id="heading-inertia-js">Inertia JS</h3>
<p>Inertia is the glue between Laravel and Vuejs that we will use to build modern single-page applications using classic server-side routing.</p>
<p>You can learn more about it in the <a target="_blank" href="https://inertiajs.com/">Documentation here</a>.</p>
<h3 id="heading-tailwind">Tailwind</h3>
<p>Tailwind CSS is a utility-first CSS framework packed with classes like flex, pt-4, text-center, and rotate-90 that you can use to build any design, directly in your markup</p>
<p>We'll use it in this project to build our design. Here's a <a target="_blank" href="https://www.freecodecamp.org/news/get-started-with-tailwindcss/">quick guide to get you up and running</a> if you aren't familiar with Tailwind.</p>
<h2 id="heading-how-to-set-up-your-machine">How to Set Up Your Machine</h2>
<p>To follow along with my live coding (and this tutorial), you will need to install Docker desktop on your machine. If you are using Windows, you will also need to enable WSL in your system settings.</p>
<p>Visit the Docker <a target="_blank" href="https://www.docker.com/get-started">getting started page</a> to install Docker Desktop.</p>
<p>If you are on Windows, enable WSL2 by following the steps <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">here</a>.</p>
<p>If you have any trouble, feel free to reach out or join my community on Slack to get help.</p>
<h2 id="heading-laravel-installation-with-sail">Laravel Installation with Sail</h2>
<p>If you have successfully installed Docker Desktop on your machine, we can open the terminal and install Laravel 9. </p>
<p>Open a terminal window and browse to a folder where you want to keep your project. Then run the command below to download the latest Laravel files. The command will put all files inside a folder called my-example-app, which you can tweak as you like.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Download laravel</span>
curl -s <span class="hljs-string">"https://laravel.build/my-example-app"</span> | bash
<span class="hljs-comment"># Enter the laravel folder</span>
<span class="hljs-built_in">cd</span> my-example-app
</code></pre>
<h3 id="heading-deploy-laravel-on-docker-using-the-sail-up-command">Deploy Laravel on Docker using the <code>sail up</code> command</h3>
<p>With Docker Desktop up and running, the next step is to start Laravel sail to build all the containers required to run our application locally.</p>
<p>Run the following command from the folder where all Laravel files have been downloaded:</p>
<pre><code class="lang-bash">vendor/bin/sail up
</code></pre>
<p>It will take a minute. Then visit <a target="_blank" href="http://localhost">http://localhost</a> and you should see your Laravel application.</p>
<p>If you run <code>sail up</code> and you get the following error, it is likely that you need to update Docker Desktop:</p>
<pre><code class="lang-bash">ERROR: Service <span class="hljs-string">'laravel.test'</span> failed to build:
</code></pre>
<h2 id="heading-how-to-build-the-app-with-laravel-9-laravel-sail-jetstram-inertia-and-vue3">How to Build the App with Laravel 9, Laravel Sail, Jetstram, Inertia and Vue3</h2>
<p>In this section, we will define a basic roadmap, install Laravel 9 with Laravel Sail, Run sail, and build the containers. </p>
<p>I will also take you on a tour of Laravel Sail and the sail commands. </p>
<p>Then we will install Jetstream and scaffold Vue and Inertia files and have a look at the files and available features.</p>
<p>Next, we will populate our database and add the front end provided by Jetstream to register an account and log into a fresh Laravel application.</p>
<p>Finally, we will have a look at the Jetstream dashboard, and the Inertia/Vue Components and then start playing around.</p>
<p>Along the way, we'll disable the registration, enable the Jetstream user profile picture feature, and then add our first Inertia page where we'll render some data taken from the database.</p>
<p>Here's the live coding video if you want to follow along that way:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/c0ibec9dhZA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>And if you prefer following along in this written tutorial, here are all the steps.</p>
<p>Just a reminder – you should have Laravel installed with Sail and have Docker set up on your machine. You can follow the steps above to do so if you haven't already.</p>
<h3 id="heading-laravel-sail-overview-sail-commands">Laravel Sail Overview – Sail Commands</h3>
<p>With Laravel Sail installed, our usual Laravel commands have sligtly changed.</p>
<p>For instance, instead of running the Laravel artisan command using PHP like <code>php artisan</code>, we now have to use Sail, like so: <code>sail artisan</code>.</p>
<p>The <code>sail artisan</code> command will return a list of all available Laravel commands.</p>
<p>Usually, when we work with Laravel, we also have to run the <code>npm</code> and <code>composer</code> commands.</p>
<p>Again, we need to prefix our commands with <code>sail</code> to make them run inside the container.</p>
<p>Below you'll find a list of some commands you will likely have to run:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Interact with the database - run the migrations</span>
sail artisan migrate <span class="hljs-comment"># It was: php artisan migrate</span>
<span class="hljs-comment"># Use composer commands</span>
sail composer require &lt;packageName&gt; <span class="hljs-comment"># it was: composer require &lt;packageName&gt;</span>
<span class="hljs-comment"># Use npm commands</span>
sail npm run dev <span class="hljs-comment"># it was: npm run dev</span>
</code></pre>
<p>You can read more in the <a target="_blank" href="https://laravel.com/docs/9.x/sail#executing-sail-commands">Sail documentation</a>.</p>
<h3 id="heading-install-jetstream-and-scaffold-vue-and-inertia">Install Jetstream and Scaffold Vue and Inertia</h3>
<p>Let's now install the Laravel Jetstream authentication package and use the Inertia scaffolding with Vue3.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> my-example-app
sail composer require laravel/jetstream
</code></pre>
<p>Remember to prefix the composer command with <code>sail</code>.</p>
<p>The command above has added a new command to Laravel. Now we need to run it to install all the Jetstream components:</p>
<pre><code class="lang-bash">sail artisan jetstream:install inertia
</code></pre>
<p>Next we need to compile all static assets with npm:</p>
<pre><code class="lang-bash">sail npm install
sail npm run dev
</code></pre>
<p>Before we can actually see our application, we will need to run the database migrations so that the session table, required by Jetstream, is present.</p>
<pre><code class="lang-bash">sail artisan migrate
</code></pre>
<p>Done! Jetstream is now installed in our application. If you visit <code>http://localhost</code> in your browser you should see the Laravel application with two links at the top to register and log in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/welcome-page.png" alt="welcome-page" width="600" height="400" loading="lazy"></p>
<h3 id="heading-populate-the-database-and-create-a-user-account">Populate the Database and Create a User Account</h3>
<p>Before creating a new user, let's have a quick look at the database configuration that Laravel Sail has created for us in the <code>.env</code> file.</p>
<pre><code class="lang-env">DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=my-example-app
DB_USERNAME=sail
DB_PASSWORD=password
</code></pre>
<p>As you can see, Laravel Sail configures everything we need to access the database container that is running on Docker. The <code>DB_DATABASE</code> is the name of the database and it is the same as the project folder. This is why in the previous step we were able to run the <code>migrate</code> command without issues.</p>
<p>Since we already migrated all database tables, we can now use the Laravel built-in user factory to create a new user then use its details to log in our user dashboard.</p>
<p>Let's open artisan tinker to interact with our application.</p>
<pre><code class="lang-bash">sail artisan tinker
</code></pre>
<p>The command above will open a command line interface that we can use to interact with our application. Let's create a new user.</p>
<pre><code class="lang-php">User::factory()-&gt;create()
</code></pre>
<p>The command above will create a new user and save its data in our database. Then it will render the user data onto the screen. Make sure to copy the user email so we can use it later to log in. Then exit by typing <code>exit;</code>.</p>
<p>The default password for every user created with a factory is <code>password</code>.</p>
<p>Let's visit the login page and access our application dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/loginpage.png" alt="loginpage" width="600" height="400" loading="lazy"></p>
<h3 id="heading-jetstream-dashboard">Jetstream Dashboard</h3>
<p>After login you are redirected to the Jetstream dashboard, which looks amazing by default. We can customize it as we like, but it is just a starting point.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/dashboard.png" alt="dashboard" width="600" height="400" loading="lazy"></p>
<h3 id="heading-jetstreamvue-components-and-inertia-overview">Jetstream/Vue Components and Inertia Overview</h3>
<p>The first thing you may notice after installing Jetstram is that there are a number of Vue components registered in our application. Not only that, also Inertia brings in Vue components. </p>
<p>To use Inertia, we need to get familiar with it when defining routes.</p>
<p>When we installed Jetstream, it created inside the <code>resources/js</code> directory a number of subfolders where all our Vue components live. There are not just simple components but also Pages components rendered by inertia as our Views.</p>
<p>The Jetstream inertia scaffolding created:</p>
<ul>
<li><code>resources/js/Jetstream</code> Here we have 27 components used by Jetstream, but we can use them in our application too if we want.</li>
<li><code>resources/js/Layouts</code> In this folder there is the layout component used by inertia to render the dashboard page</li>
<li><code>resources/js/Pages</code> This is where we will place all our Pages (views) components. You will find the Dashboard page as well as the Laravel Welcome page components here.</li>
</ul>
<p>The power of Inertia mostly comes from how it connects Vue and Laravel, letting us pass data (Database Models and more) as props to our Vue Pages components.</p>
<p>When you open the <code>routes/web.php</code> file you will notice that we no longer return a view but instead we use <code>Inertia</code> to render a Page component.</p>
<p>Let's examine the <code>/</code> homepage route that renders the Welcome component.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Welcome'</span>, [
        <span class="hljs-string">'canLogin'</span> =&gt; Route::has(<span class="hljs-string">'login'</span>),
        <span class="hljs-string">'canRegister'</span> =&gt; Route::has(<span class="hljs-string">'register'</span>),
        <span class="hljs-string">'laravelVersion'</span> =&gt; Application::VERSION,
        <span class="hljs-string">'phpVersion'</span> =&gt; PHP_VERSION,
    ]);
});
</code></pre>
<p>It looks like our usual Route definition, exept that in the closure we are returning an <code>\Inertia\Response</code> by calling the <code>render</code> method of the Inertia class <code>Inertia::render()</code>. </p>
<p>This method accepts two parameters. The first is a component name. Here we passed the <code>Welcome</code> Page component, while the second parameter is an associative array that will turn into a list of <code>props</code> to pass to the component. Here is where the magic happens.</p>
<p>Looking inside the Welcome component, you will notice that in its script section, we simply define four props matching with the keys of our associative array. Then inertia will do the rest.</p>
<pre><code class="lang-vue">&lt;script&gt;
    import { defineComponent } from 'vue'
    import { Head, Link } from '@inertiajs/inertia-vue3';

    export default defineComponent({
        components: {
            Head,
            Link,
        },
        // 👇 Define the props 
        props: {
            canLogin: Boolean, 
            canRegister: Boolean,
            laravelVersion: String,
            phpVersion: String,
        }
    })
&lt;/script&gt;
</code></pre>
<p>We can then just call the props inside the template. If you look at the template section you will notice that <code>laravelVersion</code> and <code>phpVersion</code> are referenced in the code as you normally would do with props in Vuejs.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0"</span>&gt;</span>
  Laravel v{{ laravelVersion }} (PHP v{{ phpVersion }})
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The dashboard component is a little different. In fact it uses the Layout defined under <code>Layouts/AppLayout.vue</code> and uses the <code>Welcome</code> component to render the Dashboard page content, which is the same as the laravel Welcome page.</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>
                Dashboard
            <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white overflow-hidden shadow-xl sm:rounded-lg"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">welcome</span> /&gt;</span> 
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>Inside the layout component you will notice the two inertia components <code>Head</code> and <code>Link</code>.</p>
<p>We can use the <code>Head</code> component to add head elements to our page, like meta tags, page title, and so on. The <code>Link</code> component is a wrapper aroud a standard anchor tag that incercepts click events and prevents full page reload as you can read in the Inertia documentation.</p>
<p><a target="_blank" href="https://inertiajs.com/links">Link Component</a>
<a target="_blank" href="https://inertiajs.com/title-and-meta#head-component">Head Component</a></p>
<h3 id="heading-disable-the-registration-feature">Disable the Registration Feature</h3>
<p>If you are following along, the next step I'll take is to disable one on the features Jetstream provides – register an account. </p>
<p>To do that, we can navigate to <code>config/fortify.php</code> and comment out line 135 <code>Features::registration()</code> from the features array.</p>
<pre><code class="lang-php"><span class="hljs-string">'features'</span> =&gt; [
        <span class="hljs-comment">//Features::registration(),</span>
        Features::resetPasswords(),
        <span class="hljs-comment">// Features::emailVerification(),</span>
        Features::updateProfileInformation(),
        Features::updatePasswords(),
        Features::twoFactorAuthentication([
            <span class="hljs-string">'confirmPassword'</span> =&gt; <span class="hljs-literal">true</span>,
        ]),
    ],
</code></pre>
<p>If we visit the welcome page we will notice that the <code>register</code> link is gone. Also, the route is no longer listed when we run <code>sail artisan route:list</code>.</p>
<h3 id="heading-enable-jetstream-user-profile-picture">Enable Jetstream User Profile Picture</h3>
<p>Now let's try to enable the Jetstream feature called ProfilePhotos. As you can guess, this will allow the user to add a profile picture.</p>
<p>To do that we need to visit <code>config/jetstream.php</code> and uncomment line 59 <code>Features::profilePhoto</code>.</p>
<pre><code class="lang-php">    <span class="hljs-string">'features'</span> =&gt; [
        <span class="hljs-comment">// Features::termsAndPrivacyPolicy(),</span>
        Features::profilePhotos(), <span class="hljs-comment">// 👈</span>
        <span class="hljs-comment">// Features::api(),</span>
        <span class="hljs-comment">// Features::teams(['invitations' =&gt; true]),</span>
        Features::accountDeletion(),
    ],
</code></pre>
<p>If you log in you will see that in the user profile, a new section is available to upload a profile picture.</p>
<p>But before doing anything else we need to run <code>sail artisan storage:link</code> so that Laravel creates a symlink to the <code>storage/app/public</code> folder where we will save all user profile images.</p>
<p>Now try to visit the user profile and update the profile picture. If you get a 404 on the image this is because by default Laravel sail assumes we are using Laravel valet and sets the app URL like so <code>APP_URL=http://my-example-app.test</code> in the <code>.env</code> file. Let's change it and use localhost instead.</p>
<pre><code class="lang-env">APP_URL=http://localhost
</code></pre>
<p>Now we should be good to go and be able to see and change our profile image!🥳</p>
<h3 id="heading-how-to-add-our-first-inertia-page-and-render-records-from-the-db">How to Add our First Inertia Page and Render Records from the DB</h3>
<p>Since we are rendering Vue components instead of blade views, it is wise to start <code>sail npm run watch</code> to watch and recompile our Vue components as we create or edit them. Next let's add a new Photos page.</p>
<p>I will start by creating a new Route inside web.php:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//dd(Photo::all());</span>
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Guest/Photos'</span>);
});
</code></pre>
<p>In the code above I defined a new GET route and then rendered a component that I will place inside the <code>resources/js/Pages/Guest</code> and call <code>Photos</code>. Let's create it.</p>
<p>Create a Guest folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> resources/js/Pages
mkdir Guest
<span class="hljs-built_in">cd</span> Guest
touch Photos.vue
</code></pre>
<p>Then let's define a basic component:</p>
<pre><code class="lang-vue">&lt;template&gt;
  &lt;h1&gt;Photos Page&lt;/h1&gt;
&lt;/template&gt;
</code></pre>
<p>If we visit <code>http://localhost/photos/</code> we will see our new page, cool! Let's copy over the page structure from the Welcome page so that we get the login and dashboard links as well.</p>
<p>The component will change to this:</p>
<pre><code class="lang-vue">&lt;template&gt;
    &lt;Head title="Phots" /&gt;

    &lt;div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0"&gt;
        &lt;div v-if="canLogin" class="hidden fixed top-0 right-0 px-6 py-4 sm:block"&gt;
            &lt;Link v-if="$page.props.user" :href="route('admin.dashboard')" class="text-sm text-gray-700 underline"&gt;
                Dashboard
            &lt;/Link&gt;

            &lt;template v-else&gt;
                &lt;Link :href="route('login')" class="text-sm text-gray-700 underline"&gt;
                    Log in
                &lt;/Link&gt;

                &lt;Link v-if="canRegister" :href="route('register')" class="ml-4 text-sm text-gray-700 underline"&gt;
                    Register
                &lt;/Link&gt;
            &lt;/template&gt;
        &lt;/div&gt;

        &lt;div class="max-w-6xl mx-auto sm:px-6 lg:px-8"&gt;
            &lt;h1&gt;Photos&lt;/h1&gt;

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
    import { defineComponent } from 'vue'
    import { Head, Link } from '@inertiajs/inertia-vue3';

    export default defineComponent({
        components: {
            Head,
            Link,
        },

        props: {
            canLogin: Boolean,
            canRegister: Boolean,

        }
    })
&lt;/script&gt;
</code></pre>
<p>The next step is to render a bunch of data onto this new page. For that we will build a Model and add some records to the database.</p>
<pre><code class="lang-bash">saild artisan make:model Photo -mfcr
</code></pre>
<p>This command creates a Model called <code>Photo</code>, plus a database migration table class, a factory, and a resource controller.</p>
<p>Now let's define the database table inside the migration we just creted. Visit the <code>database/migrations</code> folder and you should see a file with a name similar to this: <code>2022_02_13_215119_create_photos_table</code> (yours will be sligly different).</p>
<p>Inside the migration file we can define a basic table like the following:</p>
<pre><code class="lang-php"> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>)
    </span>{
        Schema::create(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();
            $table-&gt;string(<span class="hljs-string">'path'</span>);
            $table-&gt;text(<span class="hljs-string">'description'</span>);
            $table-&gt;timestamps();
        });
    }
</code></pre>
<p>For our table we defined just two new columns, <code>path</code> and <code>description</code>, plus the <code>id</code>, <code>created_at</code> and <code>updated_at</code> that will be created by the <code>$table-&gt;id()</code> and by the <code>$table-&gt;timestamps()</code> methods.</p>
<p>After the migration we will define a seeder and then run the migrations and seed the database.</p>
<p>At the top of the <code>database/seeders/PhotoSeeder.php</code> file we will import our Model and Faker:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Photo</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Faker</span>\<span class="hljs-title">Generator</span> <span class="hljs-title">as</span> <span class="hljs-title">Faker</span>;
</code></pre>
<p>Next we will implement the run method using a for loop to create 10 records in the database.</p>
<pre><code class="lang-php">

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params">Faker $faker</span>)
    </span>{
        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">10</span>; $i++) {
            $photo = <span class="hljs-keyword">new</span> Photo();
            $photo-&gt;path = $faker-&gt;imageUrl();
            $photo-&gt;description = $faker-&gt;paragraphs(<span class="hljs-number">2</span>, <span class="hljs-literal">true</span>);
            $photo-&gt;save();
        }
    }
</code></pre>
<p>We are ready to run the migrations and seed the database.</p>
<pre><code class="lang-php">
sail artisan migrate
sail artisan db:seed --<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PhotoSeeder</span></span>
</code></pre>
<p>We are now ready to show the data on the <code>Guest/Photos</code> page component.
First update the route and pass a collection of Photos as props to the rendered component:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//dd(Photo::all());</span>
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Guest/Photos'</span>, [
        <span class="hljs-string">'photos'</span> =&gt; Photo::all(), <span class="hljs-comment">## 👈 Pass a collection of photos, the key will become our prop in the component</span>
        <span class="hljs-string">'canLogin'</span> =&gt; Route::has(<span class="hljs-string">'login'</span>),
        <span class="hljs-string">'canRegister'</span> =&gt; Route::has(<span class="hljs-string">'register'</span>),
    ]);
});
</code></pre>
<p>Second, pass the prop to the props in the script section of the Guest/Photos component:</p>
<pre><code class="lang-js">
<span class="hljs-attr">props</span>: {
    <span class="hljs-attr">canLogin</span>: <span class="hljs-built_in">Boolean</span>,
    <span class="hljs-attr">canRegister</span>: <span class="hljs-built_in">Boolean</span>,
    <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span> <span class="hljs-comment">// 👈 Here</span>
}
</code></pre>
<p>Finally loop over the array and render all photos in the template section, just under the h1:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"photos"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"photo.id"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span> &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>Done! if you visit the <code>/photos</code> page you should see ten photos. 🥳</p>
<h2 id="heading-how-to-refactor-the-admin-dashboard-and-create-new-admin-pages">How to Refactor the Admin Dashboard and Create New Admin Pages</h2>
<p>In this chapter we will Re-route the Jetstream dashboard and make a route group for all admin pages. </p>
<p>Then we will see how to add a new link to the dashboard and add a new admin page. </p>
<p>Finally we will take a collection of data from the db and render them in a basic table. The default table isn't cool enough, so for those reading this article, I decided to add a Tailwind table component.</p>
<h3 id="heading-re-route-the-jetstream-dashboard">Re-route the Jetstream Dashboard</h3>
<p>If we look at the <code>config/fortify.php</code> file we can see that around line 64 there is a key called home. It is calling the <code>Home</code> constant of the Route service provider.</p>
<p>This means that we can tweek the constant and redirect the authenticated user to a different route.</p>
<p>Lets go through it step-by-step:</p>
<ul>
<li>update the HOME Constant</li>
<li>make a route group and redirect logged in users to <code>admin/</code> instead of '/dashboard'</li>
</ul>
<p>Our application will have only a single user, so once they're logged in it is clearly the site admin – so makes sense to redirect to an <code>admin</code> URI.</p>
<p>Change the HOME constant in <code>app/Providers/RouteServiceProvider.php</code> around line 20 to match the following:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> HOME = <span class="hljs-string">'/admin'</span>;
</code></pre>
<h3 id="heading-how-to-add-an-admin-pages-route-group">How to Add an Admin Pages Route Group</h3>
<p>Next let's update our route inside web.php. We will change the route registered by Jetstream from this:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);
</code></pre>
<p>To this:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;prefix(<span class="hljs-string">'admin'</span>)-&gt;name(<span class="hljs-string">'admin.'</span>)-&gt;group(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

    Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);

    <span class="hljs-comment">// other admin routes here</span>
});
</code></pre>
<p>The route above is a route group that uses the <code>auth:sanctum</code> middleware for all routes within the group, a prefix of <code>admin</code>, and adds a <code>admin</code> suffix to each route name.</p>
<p>The end result is that we will be able to refer to the dashboard route by name, which now will be <code>admin.dashboard</code>. When we log in, we will be redirected to the <code>admin</code> route. Our dashboard route will respond since it's URI is just <code>/</code> but the goup prefix will prefix every route in the group and make their URI start with <code>admin</code>.</p>
<p>If you now run <code>sail artisan route:list</code> you will notice that the dashboard route has changed as we expected.</p>
<p>Before moving to the next step we need to update both the <code>/layouts/AppLayout.vue</code> and <code>/Pages/Welcome.vue</code> components.</p>
<p>Do you remeber that the dashboard route name is now <code>admin.dashboard</code> and not just <code>dashboard</code>?</p>
<p>Let's inspect the two components and update every reference of <code>route('dahsboard')</code> to this:</p>
<pre><code class="lang-js">route(<span class="hljs-string">'admin.dahsboard'</span>)
</code></pre>
<p>and also every reference of <code>route().current('dashboard')</code> to this:</p>
<pre><code class="lang-js">route().current(<span class="hljs-string">'admin.dashboard'</span>)
</code></pre>
<p>After all the changes, make sure to recompile the Vue components and watch changes by running <code>sail npm run watch</code>. Then visit the home page to check if everything is working.</p>
<h3 id="heading-how-to-add-a-new-link-to-the-dashboard">How to Add a New Link to the Dashboard</h3>
<p>Now, to add a new admin page where we can list all photos stored in the database, we need to add a new route to the group we created earlier. Let's hit the <code>web.php</code> file and make our changes.</p>
<p>In the Route group we will add a new route:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;prefix(<span class="hljs-string">'admin'</span>)-&gt;name(<span class="hljs-string">'admin.'</span>)-&gt;group(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

    Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);

    <span class="hljs-comment">// 👇 other admin routes here 👇</span>

    Route::get(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/Photos'</span>);
    })-&gt;name(<span class="hljs-string">'photos'</span>); <span class="hljs-comment">// This will respond to requests for admin/photos and have a name of admin.photos</span>

});
</code></pre>
<p>In the new route above we used the <code>inertia()</code> helper function that does the same exact thing – returns an Inertia/Response and renders our Page component. We placed the component under an <code>Admin</code> folder inside <code>Pages</code> and we will call it <code>Photos.vue</code>.</p>
<p>Before we create the component, let's add a new link to the dashboard that points to our new route.</p>
<p>Inside <code>AppLayout.vue</code>, find the <code>Navigation Links</code> comment and copy/paste the <code>jet-nav-link</code> component that is actually displaing a link to the dashboard and make it point to our new route.</p>
<p>You will end up having something like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Navigation Links --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">jet-nav-link</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.dashboard')"</span> <span class="hljs-attr">:active</span>=<span class="hljs-string">"route().current('admin.dashboard')"</span>&gt;</span>
        Dashboard
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-nav-link</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 👇 here it is our new link --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">jet-nav-link</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.photos')"</span> <span class="hljs-attr">:active</span>=<span class="hljs-string">"route().current('admin.photos')"</span>&gt;</span>
        Photos
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-nav-link</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our link above uses <code>route('admin.photos')</code> to point to the correct route in the admin group.</p>
<p>If you visit <code>localhost/dashboard</code> and open the inspector, you should see an error:</p>
<pre><code class="lang-js"><span class="hljs-built_in">Error</span>: Cannot find <span class="hljs-built_in">module</span> <span class="hljs-string">`./Photos.vue`</span>
</code></pre>
<p>It is fine – we haven't created the Photos page component yet. So let's do it now!</p>
<h3 id="heading-how-to-add-a-new-admin-page-component">How to Add a New Admin Page Component</h3>
<p>Make a file named <code>Photos.vue</code> inside the <code>Pages/Admin</code> folder. Below are the bash commands to create the folder and the file via terminal, but you can do the same using your IDE's graphical interface.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> resources/js/Pages
mkdir Admin
touch Admin/Photos.vue
</code></pre>
<p>To make this new page look like the Dashboard page, we will copy over its content. You should end up having something like this:</p>
<pre><code class="lang-vue">
&lt;template&gt;
  &lt;app-layout title="Dashboard"&gt; &lt;!-- 👈 if you want you can update the page title --&gt;
    &lt;template #header&gt;
      &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
    &lt;/template&gt;

    &lt;div class="py-12"&gt;
      &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
        &lt;div class="bg-white overflow-hidden shadow-xl sm:rounded-lg"&gt;
          &lt;!-- 👇  All photos for the Admin page down here --&gt;
          &lt;h1 class="text-2xl"&gt;Photos&lt;/h1&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/app-layout&gt;
&lt;/template&gt;

&lt;script&gt;
import { defineComponent } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";

export default defineComponent({
  components: {
    AppLayout,
  },
});
&lt;/script&gt;
</code></pre>
<p>I removed a few pieces from the Dashboard template so make sure to double check the code above. The <code>welcome</code> component was removed from the template as it is not required in this page, and also its reference in the script section. The rest is identical.</p>
<p>Feel free to update the page title referenced as prop on the <code>&lt;app-layout title="Dashboard"&gt;</code>.</p>
<p>Now when you visit <code>localhost/admin</code> you can click on the Photos menu item and see our Photos page component content. It's not much for now, just an <code>h1</code>.</p>
<h3 id="heading-how-to-render-records-in-the-admin-page-as-a-table">How to Render Records in the Admin Page as a Table</h3>
<p>Now it's time to render the data onto a table. To make things work let's first add our markup and fake that we already have access to as an array of objects and loop over them inside our table. Than we will figure out how to make things work for real.</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table-auto w-full text-left"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Photo<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Desciption<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Actions<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ photo.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"60"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{photo.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>View - Edit - Delete<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<p>Ok, since we assumed that our component has access to a list of Photos, let's pass a new prop to the component from the Route.</p>
<p>Update the route in web.php and pass to the <code>inertia()</code> function a second argument that will be an associative array. It will have its keys passed as props to the Vue Page component. </p>
<p>In it we will call <code>Photo::all()</code> to have a collection to assign to a <code>photos</code> key, but you can use other eloquent methods if you want to paginate the results, for example.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/Photos'</span>, [
        <span class="hljs-string">'photos'</span> =&gt; Photo::all()
    ]);
})-&gt;name(<span class="hljs-string">'photos'</span>);
</code></pre>
<p>To connect the prop to our Page component we need to define the prop also inside the component.</p>
<pre><code class="lang-js">&lt;script&gt;
<span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
  <span class="hljs-attr">components</span>: {
    AppLayout,
  },
  <span class="hljs-comment">/* 👇 Pass the photos array as a props 👇 */</span>
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span>,
  },
});
&lt;/script&gt;
</code></pre>
<h4 id="heading-extra-how-to-use-a-tailwind-table-component">Extra: How to use a Tailwind table component</h4>
<p>Tailwind is a CSS framework similar to Bootstrap. There are a number of free to use components that we can grab from the documentation, tweak, and use.</p>
<p>This table component is free and looks nice:<a target="_blank" href="https://tailwindui.com/components/application-ui/lists/tables">https://tailwindui.com/components/application-ui/lists/tables</a>.</p>
<p>We can tweek the Photos page template and use the Tailwind table component to get a nice looking table like so:</p>
<pre><code class="lang-vue">
&lt;template&gt;
    &lt;app-layout title="Dashboard"&gt;
        &lt;template #header&gt;
            &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
        &lt;/template&gt;

         &lt;div class="py-12"&gt;
            &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
              &lt;!-- All posts goes here --&gt;
              &lt;h1 class="text-2xl"&gt;Photos&lt;/h1&gt;
              &lt;a class="px-4 bg-sky-900 text-white rounded-md" href&gt;Create&lt;/a&gt;
              &lt;div class="flex flex-col"&gt;
                  &lt;div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"&gt;
                      &lt;div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"&gt;
                          &lt;div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"&gt;
                              &lt;table class="min-w-full divide-y divide-gray-200"&gt;
                                  &lt;thead class="bg-gray-50"&gt;
                                      &lt;tr&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;ID&lt;/th&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;Photos&lt;/th&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;Description&lt;/th&gt;
                                          &lt;th scope="col" class="relative px-6 py-3"&gt;
                                              &lt;span class="sr-only"&gt;Edit&lt;/span&gt;
                                          &lt;/th&gt;
                                      &lt;/tr&gt;
                                  &lt;/thead&gt;
                                  &lt;tbody class="bg-white divide-y divide-gray-200"&gt;
                                      &lt;tr v-for="photo in photos" :key="photo.id"&gt;
                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div
                                                  class="text-sm text-gray-900"
                                              &gt;{{ photo.id }}&lt;/div&gt;
                                          &lt;/td&gt;

                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div class="flex items-center"&gt;
                                                  &lt;div class="flex-shrink-0 h-10 w-10"&gt;
                                                      &lt;img
                                                          class="h-10 w-10 rounded-full"
                                                          :src="photo.path"
                                                          alt
                                                      /&gt;
                                                  &lt;/div&gt;
                                              &lt;/div&gt;
                                          &lt;/td&gt;

                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div class="text-sm text-gray-900"&gt;
                                                {{ photo.description.slice(0, 100) + '...' }}
                                              &lt;/div&gt;
                                          &lt;/td&gt;
                                        &lt;!-- ACTIONS --&gt;
                                          &lt;td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"&gt;
                                              &lt;a href="#" class="text-indigo-600 hover:text-indigo-900"&gt;
                                              View - Edit - Delete
                                              &lt;/a&gt;
                                          &lt;/td&gt;
                                      &lt;/tr&gt;
                                  &lt;/tbody&gt;
                              &lt;/table&gt;
                          &lt;/div&gt;
                      &lt;/div&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/app-layout&gt;
&lt;/template&gt;
</code></pre>
<h2 id="heading-how-to-submit-forms-with-files">How to Submit Forms with Files</h2>
<p>For the next section we will look into how to submit a form so that we can add a new photo to the database.</p>
<ul>
<li>Add a create button</li>
<li>Add a create route</li>
<li>Define the PhotosCreate component</li>
<li>Add a form</li>
<li>Validate data</li>
<li>Show validation errors</li>
<li>Save the file to the filesystem</li>
<li>Save the model</li>
</ul>
<h3 id="heading-how-to-create-a-new-photo">How to Create a New Photo</h3>
<p>Add a link that points to a create route:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 bg-sky-900 text-white rounded-md"</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.photos.create')"</span>&gt;</span>Create<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Create the route within the admin group:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/photos/create'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/PhotosCreate'</span>);
})-&gt;name(<span class="hljs-string">'photos.create'</span>);
</code></pre>
<p>Let's add also the route that will handle the form submission for later:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    dd(<span class="hljs-string">'I will handle the form submission'</span>)   
})-&gt;name(<span class="hljs-string">'photos.store'</span>);
</code></pre>
<p>Create the <code>Admin/PhotosCreate.vue</code> component:</p>
<pre><code class="lang-vue">
    &lt;template&gt;
    &lt;app-layout title="Dashboard"&gt;
        &lt;template #header&gt;
            &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
        &lt;/template&gt;

         &lt;div class="py-12"&gt;
            &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
                &lt;h1 class="text-2xl"&gt;Add a new Photo&lt;/h1&gt;
                &lt;!-- 👇 Photo creation form goes here --&gt;

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/app-layout&gt;
&lt;/template&gt;


&lt;script&gt;
import { defineComponent } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";

export default defineComponent({
  components: {
    AppLayout,
  },

});
&lt;/script&gt;
</code></pre>
<h2 id="heading-how-to-add-the-form-to-the-component">How to Add the Form to the Component</h2>
<p>The next step is to add the form to the page and figure out how to submit it.</p>
<p>If you hit the Inertia documentation you will find out that there is a useForm class that we can use to simplify the process.</p>
<p>First, import the module inside the script tag of the Admin/PhotosCreate.vue component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
</code></pre>
<p>Next we can use it in the setup function (Vue 3 composition API):</p>
<pre><code class="lang-js">setup () {
    <span class="hljs-keyword">const</span> form = useForm({
      <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
    })

    <span class="hljs-keyword">return</span> { form }
  }
</code></pre>
<p>In the code above we defined the function called <code>setup()</code> then a constant called <code>form</code> to have the <code>useForm()</code> class assigned to it.</p>
<p>Inside its parentheses we defined two properties, <code>path</code> and <code>description</code> which are the column names of our photos model.</p>
<p>Finally we returned the <code>form</code> variable for the setup function. This is to make the variable available inside our template.</p>
<p>Next we can add the form markup:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"form.post(route('admin.photos.store'))"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span> Description <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"lorem ipsum"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"form.description"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-sm text-gray-500"</span>&gt;</span>Brief description for your photo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.description"</span>&gt;</span>{{form.errors.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span> Photo <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-1 text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-auto h-12 w-12 text-gray-400"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 48 48"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex text-sm text-gray-600"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Upload a file<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span> @<span class="hljs-attr">input</span>=<span class="hljs-string">"form.path = $event.target.files[0]"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-1"</span>&gt;</span>or drag and drop<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>PNG, JPG, GIF up to 10MB<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.path"</span>&gt;</span>{{form.errors.path}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"form.processing"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>&gt;</span>Save<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>The code above uses the Vue v-on directive short end syntax <code>@submit.prevent="form.post(route('admin.photos.store'))"</code> on the form tag, and the dom event <code>submit</code> with the <code>prevent</code> modifier. </p>
<p>Then it uses the <code>form</code> variable that we created earlier and a <code>post</code> method. This is available because we are using the <code>useForm</code> class. </p>
<p>Next we point the form to the route named admin.photos.store that we created earlier.</p>
<p>Inside the form we have two groups of inputs. First, we have the textarea that uses the v-model to bind it to the property <code>form.description</code> that we declared before.</p>
<p>The second group uses the <code>form.path</code> in a Tailwind component (showing the markup for a drop file area).</p>
<p>Right now we are allowing users to upload only a single photo using the v-on directive on the input DOM event <code>@input="form.path = $event.target.files[0]"</code>.</p>
<p>The last two things to notice are the error handling done via <code>&lt;div class="text-red-500" v-if="form.errors.path"&gt;{{form.errors.path}}&lt;/div&gt;</code> for the path and also for the description.</p>
<p>Finally we use <code>form.processing</code> to disable the submit button while the form is processing.</p>
<p>The next step is to define the logic to save the data inside the database.</p>
<h2 id="heading-how-to-store-data">How to Store Data</h2>
<p>To store the data, we can edit the route we defined earlier like so:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Request $request</span>) </span>{
    <span class="hljs-comment">//dd('I will handle the form submission')  </span>

    <span class="hljs-comment">//dd(Request::all());</span>
    $validated_data = $request-&gt;validate([
        <span class="hljs-string">'path'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'max:2500'</span>],
        <span class="hljs-string">'description'</span> =&gt; [<span class="hljs-string">'required'</span>]
    ]);
    <span class="hljs-comment">//dd($validated_data);</span>
    $path = Storage::disk(<span class="hljs-string">'public'</span>)-&gt;put(<span class="hljs-string">'photos'</span>, $request-&gt;file(<span class="hljs-string">'path'</span>));
    $validated_data[<span class="hljs-string">'path'</span>] = $path;
    <span class="hljs-comment">//dd($validated_data);</span>
    Photo::create($validated_data);
    <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
})-&gt;name(<span class="hljs-string">'photos.store'</span>);
</code></pre>
<p>The code above uses dependency injection to allow us to use the parameter <code>$request</code> inside the callback function. </p>
<p>We first validate the request and save the resulting array inside the variable <code>$validated_data</code>. Then we use the <code>Storage</code> facades to save the file in the filesystem and obtain the file path that we store inside the <code>$path variable</code>.</p>
<p>Finally we add a <code>path</code> key to the associative array and pass to it the <code>$path</code> variable. Next we create the resource in the database using the <code>Photo::create</code> method and redirect the user to the <code>admin.photos</code> page using the new <code>to_route()</code> helper function.</p>
<p>Make sure to import the <code>Request</code> class and the <code>Storage</code> facades at the top of the web.php file like so:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Storage</span>;
</code></pre>
<p>Now we can add a new photo in the database and show a list of photos for both the admin and standard visitors.</p>
<p>Next we need to complete the CRUD operations and allow the user to edit/update a photo and delete it.</p>
<h2 id="heading-how-to-update-operations">How to Update Operations</h2>
<p>Let's start by adding the routes responsible for showing the forms used to edit the resource and update its values onto the database.</p>
<p>Just under the other routes in the Admin group, let's add the following code:</p>
<pre><code class="lang-php">
Route::get(<span class="hljs-string">'/photos/{photo}/edit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Photo $photo</span>)</span>{
     <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/PhotosEdit'</span>, [
            <span class="hljs-string">'photo'</span> =&gt; $photo
        ]);
})-&gt;name(<span class="hljs-string">'photos.edit'</span>);
</code></pre>
<p>The route above uses dependency injection to inject inside the function the current post, selected by the URI <code>/photos/{photo}/edit</code>. </p>
<p>Next it returns the Inertia response via the <code>inertia()</code> function that accepts the Component name <code>'Admin/PhotosEdit'</code> as its first parameter and an associative array as its second.</p>
<p>Doing <code>['photo' =&gt; $photo]</code> will allow us to pass the <code>$photo</code> model as a prop to the component later.</p>
<p>Next let's add the new Page component under <code>resources/js/Pages/Admin/PhotosEdit.vue</code></p>
<p>This will be its template:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Edit Photo"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>Edit Photo<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"form.post(route('admin.photos.update', photo.id))"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                            <span class="hljs-attr">for</span>=<span class="hljs-string">"description"</span>
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>
                        &gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                                <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span>
                                <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span>
                                <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span>
                                <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"</span>
                                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"lorem ipsum"</span>
                                <span class="hljs-attr">v-model</span>=<span class="hljs-string">"form.description"</span>
                            /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-sm text-gray-500"</span>&gt;</span>Brief description for your photo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>
                            <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.description"</span>
                        &gt;</span>{{ form.errors.description }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-cols-2"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preview p-4"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"'/storage/' + photo.path"</span> <span class="hljs-attr">alt</span> /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span>Photo<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                                <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"</span>
                            &gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-1 text-center"</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                                        <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-auto h-12 w-12 text-gray-400"</span>
                                        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                                        <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
                                        <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 48 48"</span>
                                        <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>
                                    &gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                                            <span class="hljs-attr">d</span>=<span class="hljs-string">"M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"</span>
                                            <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
                                            <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>
                                            <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span>
                                        /&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex text-sm text-gray-600"</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                                            <span class="hljs-attr">for</span>=<span class="hljs-string">"path"</span>
                                            <span class="hljs-attr">class</span>=<span class="hljs-string">"relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"</span>
                                        &gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Upload a file<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                                                <span class="hljs-attr">id</span>=<span class="hljs-string">"path"</span>
                                                <span class="hljs-attr">name</span>=<span class="hljs-string">"path"</span>
                                                <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span>
                                                <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>
                                                @<span class="hljs-attr">input</span>=<span class="hljs-string">"form.path = $event.target.files[0]"</span>
                                            /&gt;</span>
                                        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-1"</span>&gt;</span>or drag and drop<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>PNG, JPG, GIF up to 10MB<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.path"</span>&gt;</span>{{ form.errors.path }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
                        <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"form.processing"</span>
                        <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>
                    &gt;</span>Update<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>The template is actually identical to the Create component, except for a few things. The form points to a route that expects a paramenter that we pass as the second argument to the funtion <code>route</code>. It looks like this: <code>&lt;form @submit.prevent="form.post(route('admin.photos.update', photo.id))"&gt;</code>.</p>
<p>There is a section where we can see the original photo next to the upload form group:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preview p-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"'/storage/' + photo.path"</span> <span class="hljs-attr">alt</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The rest is identical, and here we have the script section:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
    <span class="hljs-attr">components</span>: {
        AppLayout,
    },
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">photo</span>: <span class="hljs-built_in">Object</span>
    },
    setup(props) {
        <span class="hljs-keyword">const</span> form = useForm({
            <span class="hljs-attr">_method</span>: <span class="hljs-string">"PUT"</span>,
            <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">description</span>: props.photo.description,
        })

        <span class="hljs-keyword">return</span> { form }
    },

});
</code></pre>
<p>Notice that we are passing a props object with the photo key, which allows us to reference the model in the template.</p>
<p>Next, this <code>_method: "PUT",</code> line of code is required to be able to submit a <code>PUT</code> request instead of the <code>POST</code> request called on the form tag.</p>
<p>Now let's implement the logic to handle the form submission inside the Route below.</p>
<p>In web.php just under the previous route, let's add one that responds to the PUT request submitted by our form.</p>
<pre><code class="lang-php">Route::put(<span class="hljs-string">'/photos/{photo}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Request $request, Photo $photo</span>)
    </span>{
        <span class="hljs-comment">//dd(Request::all());</span>

        $validated_data = $request-&gt;validate([
            <span class="hljs-string">'description'</span> =&gt; [<span class="hljs-string">'required'</span>]
        ]);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'path'</span>)) {
            $validated_data[<span class="hljs-string">'path'</span>] = $request-&gt;validate([
                <span class="hljs-string">'path'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'max:1500'</span>],

            ]);

            <span class="hljs-comment">// Grab the old image and delete it</span>
            <span class="hljs-comment">// dd($validated_data, $photo-&gt;path);</span>
            $oldImage = $photo-&gt;path;
            Storage::delete($oldImage);

            $path = Storage::disk(<span class="hljs-string">'public'</span>)-&gt;put(<span class="hljs-string">'photos'</span>, $request-&gt;file(<span class="hljs-string">'path'</span>));
            $validated_data[<span class="hljs-string">'path'</span>] = $path;
        }

        <span class="hljs-comment">//dd($validated_data);</span>

        $photo-&gt;update($validated_data);
        <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
    })-&gt;name(<span class="hljs-string">'photos.update'</span>);
</code></pre>
<p>The route logic is straigthforward. First we validate the description, next we check if a file was uploaded and if so we validate it. </p>
<p>Then we delete the previously uploaded image <code>Storage::delete($oldImage);</code> before storing the new image onto the datadabse and update the resource using <code>$photo-&gt;update($validated_data);</code>.</p>
<p>As before with the store route, we redirect to the <code>admin.photos</code> route using <code>return to_route('admin.photos');</code>.</p>
<h2 id="heading-how-to-delete-a-resource">How to Delete a Resource</h2>
<p>The last step we need to take is to write the logic to delete the photo. Let's start by adding the route.</p>
<p>Right below the previous route we can write:</p>
<pre><code class="lang-php">Route::delete(<span class="hljs-string">'/photos/{photo}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Photo $photo</span>)
</span>{
    Storage::delete($photo-&gt;path);
    $photo-&gt;delete();
    <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
})-&gt;name(<span class="hljs-string">'photos.delete'</span>);
</code></pre>
<p>This route is also using a wildcard in its URI to identify the resource. Next, its second paramenter is the callback that uses the dependency injection as before. Inside the callback we first delete the image from the filesystem using <code>Storage::delete($photo-&gt;path);</code>.</p>
<p>Then we remove the resource from the database <code>$photo-&gt;delete();</code> and redirect the user back <code>return to_route('admin.photos');</code> like we did in the previous reoute.</p>
<p>Now we need to add a delete button to the table we created in one of the previous steps to show all photos.</p>
<p>Inside the template section of the component <code>Admin/Photos.vue</code> within the <code>v-for</code>, we can add this Jetstream button:</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
    Delete
<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
</code></pre>
<p>Find the table cell that has the <code>ACTIONS</code> comment and replace the <code>DELETE</code> text with the button above.</p>
<p>So the final code will be:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
    View - Edit - 

    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
        Delete
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<p>As you can see there is a <code>@click</code> event listener on the button. It calls a method <code>delete_photo(photo)</code> that we need to define along with a bunch of other methods to have a nice modal opening to ask for confirmation from the user.</p>
<p>First import the Inertia helper function useForm:</p>
<pre><code class="lang-js"><span class="hljs-comment">// 0. Import the useForm class at the top of the script section along with all required components</span>
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> JetDangerButton <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DangerButton.vue'</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
</code></pre>
<p>Remember to register the component <code>JetDangerButton</code> inside the components object before moving forward.</p>
<p>Next add the <code>setup()</code> function in the script section and implement the logic required to submit the form and show a modal. The comments in the code will guide you thorought all the steps.</p>
<pre><code class="lang-js"><span class="hljs-comment">// 1. add the setup function</span>
setup() {
    <span class="hljs-comment">// 2. declare a form variable and assign to it the Inertia useForm() helper function </span>
    <span class="hljs-keyword">const</span> form = useForm({
        <span class="hljs-comment">// 3. override the form method to make a DELETE request</span>
        <span class="hljs-attr">_method</span>: <span class="hljs-string">"DELETE"</span>,
    });
    <span class="hljs-comment">// 4. define a reactive object with show_modal and photo property</span>
    <span class="hljs-comment">// this will be used to figure out when to show the modal and the selected post values</span>
    <span class="hljs-keyword">const</span> data = ref({
        <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">photo</span>: {
            <span class="hljs-attr">id</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
        }
    })

    <span class="hljs-comment">// 5. define the delete_photo function and update the values of the show_modal and photo properties</span>
    <span class="hljs-comment">// of the reactive object defined above. This method is called by the delete button and will record the details </span>
    <span class="hljs-comment">// of the selected post</span>
    <span class="hljs-keyword">const</span> delete_photo = <span class="hljs-function">(<span class="hljs-params">photo</span>) =&gt;</span> {
        <span class="hljs-comment">//console.log(photo);</span>
        <span class="hljs-comment">//console.log(photo.id, photo.path, photo.description);</span>
        data.value = {
            <span class="hljs-attr">photo</span>: {
                <span class="hljs-attr">id</span>: photo.id,
                <span class="hljs-attr">path</span>: photo.path,
                <span class="hljs-attr">description</span>: photo.description
            },
            <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">true</span>
        };
    }
    <span class="hljs-comment">// 6. define the method that will be called when our delete form is submitted</span>
    <span class="hljs-comment">// the form will be created next</span>
    <span class="hljs-keyword">const</span> deleting_photo = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
        form.post(route(<span class="hljs-string">'admin.photos.delete'</span>, id))
        closeModal();
    }
    <span class="hljs-comment">// 7. delare a method to close the modal by setting the show_modal to false</span>
    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
        data.value.show_modal = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-comment">// 8. remember to return from the setup function the all variables and methods that you want to expose </span>
    <span class="hljs-comment">// to the template.</span>
    <span class="hljs-keyword">return</span> { form, data, closeModal, delete_photo, deleting_photo }

}
</code></pre>
<p>Finally outside the <code>v-for</code> loop add the modal using the following code. You can place this where you want but not inside the loop.</p>
<pre><code class="lang-html">
 <span class="hljs-tag">&lt;<span class="hljs-name">JetDialogModal</span> <span class="hljs-attr">:show</span>=<span class="hljs-string">"data.show_modal"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">title</span>&gt;</span>
        Photo {{ data.photo.description.slice(0, 20) + '...' }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">content</span>&gt;</span>
        Are you sure you want to delete this photo?

    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"closeModal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 py-2"</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"deleting_photo(data.photo.id)"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Yes, I am sure!<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">JetDialogModal</span>&gt;</span>
</code></pre>
<p>This is our final JavaScript code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;
<span class="hljs-keyword">import</span> TableComponent <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Components/TableComponent.vue"</span>;
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> JetDialogModal <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DialogModal.vue'</span>;
<span class="hljs-keyword">import</span> JetDangerButton <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DangerButton.vue'</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
    <span class="hljs-attr">components</span>: {
        AppLayout,
        Link,
        TableComponent,
        JetDialogModal,
        JetDangerButton
    },
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span>,
    },

    setup() {

        <span class="hljs-keyword">const</span> form = useForm({
            <span class="hljs-attr">_method</span>: <span class="hljs-string">"DELETE"</span>,
        });
        <span class="hljs-keyword">const</span> data = ref({
            <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">photo</span>: {
                <span class="hljs-attr">id</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
            }

        })


        <span class="hljs-keyword">const</span> delete_photo = <span class="hljs-function">(<span class="hljs-params">photo</span>) =&gt;</span> {
            <span class="hljs-comment">//console.log(photo);</span>
            <span class="hljs-built_in">console</span>.log(photo.id, photo.path, photo.description);
            data.value = {
                <span class="hljs-attr">photo</span>: {
                    <span class="hljs-attr">id</span>: photo.id,
                    <span class="hljs-attr">path</span>: photo.path,
                    <span class="hljs-attr">description</span>: photo.description
                },
                <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">true</span>
            };
        }
        <span class="hljs-keyword">const</span> deleting_photo = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
            form.post(route(<span class="hljs-string">'admin.photos.delete'</span>, id))
            closeModal();
        }

        <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
            data.value.show_modal = <span class="hljs-literal">false</span>;


        }

        <span class="hljs-keyword">return</span> { form, data, closeModal, delete_photo, deleting_photo }

    }
});
&lt;/script&gt;
</code></pre>
<p>And here we have the HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

         <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
              <span class="hljs-comment">&lt;!-- All posts goes here --&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl"</span>&gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 bg-sky-900 text-white rounded-md"</span> <span class="hljs-attr">href</span>&gt;</span>Create<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"</span>&gt;</span>
                              <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"min-w-full divide-y divide-gray-200"</span>&gt;</span>
                                  <span class="hljs-tag">&lt;<span class="hljs-name">thead</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-50"</span>&gt;</span>
                                      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative px-6 py-3"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                                  <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
                                  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white divide-y divide-gray-200"</span>&gt;</span>
                                      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"photo.id"</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                                                  <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-900"</span>
                                              &gt;</span>{{ photo.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
                                                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-shrink-0 h-10 w-10"</span>&gt;</span>
                                                      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                                                          <span class="hljs-attr">class</span>=<span class="hljs-string">"h-10 w-10 rounded-full"</span>
                                                          <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span>
                                                          <span class="hljs-attr">alt</span>
                                                      /&gt;</span>
                                                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-900"</span>&gt;</span>
                                                {{ photo.description.slice(0, 100) + '...' }}
                                              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                        <span class="hljs-comment">&lt;!-- ACTIONS --&gt;</span>
                                         <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
                                            View - Edit - 

                                            <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
                                                Delete
                                            <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
                                            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                                  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
                              <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
                          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">JetDialogModal</span> <span class="hljs-attr">:show</span>=<span class="hljs-string">"data.show_modal"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">title</span>&gt;</span>
                Photo {{ data.photo.description.slice(0, 20) + '...' }}
            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">content</span>&gt;</span>
                Are you sure you want to delete this photo?

            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"closeModal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 py-2"</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"deleting_photo(data.photo.id)"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Yes, I am sure!<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">JetDialogModal</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>That's it. If you did everything correctly you should be able to see all photos, create new photos as well as edit and delete them.</p>
<p>I will leave you some home work. Can you figure out how to implement the view and edit links before the delete button in the section below?</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- ACTIONS --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
    View - Edit - 

    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
        Delete
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<h2 id="heading-wrapup-and-whats-next">Wrapup and What's next</h2>
<p>During this guide we took our first steps and learned how to build a single page application using Laravel as our backend framework and Vue3 for the front end. We glued them together with Inertia js and built a simple photo application that lets a user manage photos. </p>
<p>We are just at the beginning of a fantastic journey. Learning new technologies isn't easy, but thanks to their exaustive documentations we can keep up and build awesome applications. </p>
<p>Your next step to master Laravel, Vue3, Inertia and all the tech we have been using so far is to hit their documentation and keep learning. Use the app we have build if you want, and improve it or start over from scratch. </p>
<p>Just keep that in mind, coding is fun so relax and enjoy it.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This is just an overview of how I'd build a single page application using these technologies.</p>
<p>If you are familiar with server-side routing and Vuejs then you will enjoy bulding a single page application with Laravel, Inertia, and Vuejs. The learning curve isn't that steep plus you have great documentation to help you out.</p>
<p>I hope you've enjoyed this guide. If so, let me know and consider subscribing to my YouTube channel and following me on Twitter. And if you get stuck, get in touch for help.</p>
<p>You can find the source code for this guide <a target="_blank" href="https://bitbucket.org/fbhood/spa-with-laravel-9/src/master/">here</a>.</p>
<p><a target="_blank" href="https://twitter.com/Fab_Sky_Walker">Follow me on Twitter</a>
<a target="_blank" href="https://join.slack.com/t/fabiopacificicom/shared_invite/zt-rf4vwvcm-esx1RkokwrJ93yyr1rPpVQ">Join me on slack</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Serverless Online Payments with Netlify and Stripe ]]>
                </title>
                <description>
                    <![CDATA[ By Alain Perkaz One of the first steps many young startups take is setting up a static web page, perhaps with an email newsletter, to help them build an audience.  As the weeks go by and the MVP is getting further along, the subject of how to handle ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/serverless-online-payments/</link>
                <guid isPermaLink="false">66d45d9ccc7f04d2549a3728</guid>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ payments ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 17 Nov 2021 20:54:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/11/----1--2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alain Perkaz</p>
<p>One of the first steps many young startups take is setting up a static web page, perhaps with an email newsletter, to help them build an audience. </p>
<p>As the weeks go by and the MVP is getting further along, the subject of how to handle payments will eventually emerge.</p>
<p>From one-time payments to SaaS subscriptions, supporting online payments can be daunting and time-consuming. This post will introduce you to an easy way to process online payments with Stripe, without any extra infrastructure other than a static web page.</p>
<p>You don't need a custom backend to store the payment information, or cron jobs to send invoices, and there's no need to track customers in a separate database. This is perfect if you are a single-founder or early-stage startup that wants to validate the idea without creating a custom solution.</p>
<p>Sounds good? Let's dive in! 🤿</p>
<h2 id="heading-high-level-overview-of-the-project">High-level overview of the project</h2>
<p>For the sake of this article, we'll define an early-stage startup use case, where this kind of serverless online payment setup will bring the most value (low cost and fast to implement).</p>
<p>Let's imagine our startup idea is to self-publish a book. As the book is being finalized, we would like to open the lifetime access to the book as a pre-sale.</p>
<p>We will need a way to process payments for lifetime access to the book. For this, we'll need a payment processor and perhaps a way to run some logic away from the client (for example, leveraging the payment processor's API).</p>
<h3 id="heading-payment-processor">Payment processor</h3>
<p>There are plenty of payment processors available, each with different terms, support for payment methods, and public APIs. For our serverless online payment processor, we'll use <a target="_blank" href="https://stripe.com/">Stripe</a>. I chose to use Stripe for two reasons:</p>
<p>First, Stripe is an industry-leading payment processor with an excellent API. Their API is extensively documented, and they offer integration SDKs for many languages (JS included). Setting it up is entirely free, and you only pay a small commission per processed transaction.</p>
<p>Second, Stripe offers Stripe Checkout, a free product specifically built to boost conversions and support various payment options. It's dead-simple to integrate and comes with a great UI.</p>
<h3 id="heading-what-about-the-server">What about the server?</h3>
<p>To be clear, Stripe requires some server-side code to generate a session once a user inputs their payment data. The session is available to the developer to perform payment-related operations (without exposing the sensitive payment details).</p>
<p>Before you get really upset with me, let me clarify that we won't need to set up a dedicated server 😅. It may seem a bit contradictory, but Stripe requires that some of the interaction code is in a server-<strong>like</strong> environment (serverless computing to the rescue!).</p>
<p>Luckily for us, this is 2021, and there are quite a few options to execute on-demand server-side code. Most cloud providers offer this functionality (AWS lambdas, Google Cloud cloud functions, Azure functions…you name it).</p>
<p>Since our startup already has a web page, we'll use <a target="_blank" href="https://www.netlify.com/products/functions/">Netlify functions</a>. It will allow us to run the server-side code with almost no extra configuration, and it plays nicely with the existing web page statics. </p>
<p>The paradigm of combining static web assets with on-demand serverless functions is part of the <a target="_blank" href="https://jamstack.org/">JAM Stack</a> (we'll leave that for another post). Keep reading for the detailed instruction on how to set up serverless payments.</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635610448861_image.png" alt="Image" width="600" height="400" loading="lazy">
<em>High-level schema of the solution</em></p>
<h1 id="heading-step-by-step-project-setup">Step-by-step project setup</h1>
<p>Great, now that you have a clear picture of the problem space and the tools we'll use to build our solution, let's build it. 🛠</p>
<p>The complete code example is available at <a target="_blank" href="https://github.com/aperkaz/serverless-payments">https://github.com/aperkaz/serverless-payments</a>.</p>
<h3 id="heading-how-to-set-up-netlify">How to set up Netlify</h3>
<p>First, create a <a target="_blank" href="https://www.netlify.com/">Netlify</a> account (if you don't have one already). The free tier is enough for moderate usage, so no need to worry about that. </p>
<p>Netlify provides CI/CD for automated deployments of our webpage and serverless functions by connecting to a Git repo in Github / Gitlab / Bitbucket. So, let's create a repo in one of those providers with your website assets.</p>
<p>Next, install the <a target="_blank" href="https://cli.netlify.com/getting-started/">Netlify CLI</a>. It will ask you some questions and request access to your Netlify and Git repo provider (GitHub in my case).</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635614098858_Screenshot+2021-10-30+at+19.14.47.png" alt="Image" width="600" height="400" loading="lazy">
<em>Installing the CLI with <code>npm install netlify-cli -g</code></em></p>
<p>At this point, we can push to the repository’s <code>main</code> / <code>master</code> branch, and Netlify will automatically deploy. You can run <code>netlify open</code> in the console to open Netlify’s admin panel, and visit the deployed URL.</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635614710558_Screenshot+2021-10-30+at+19.19.40.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Excellent, with the auto-deploy ready, now let's set up Stripe. 💸</p>
<h3 id="heading-how-to-set-up-stripe">How to set up Stripe</h3>
<p>Create an account in Stripe, validate the email, and sign in. Then, <a target="_blank" href="https://stripe.com/docs/keys">generate a set of API keys</a> (Secret key and Publishable key).</p>
<p>You have to be careful with those keys and never commit the Secret key in the code. Since we will need it in our server-side code, we'll keep it as an <a target="_blank" href="https://www.netlify.com/blog/2021/07/12/managing-environment-variables-from-your-terminal-with-netlify-cli/">environment variable</a>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new env variable in Netlify</span>
netlify env:<span class="hljs-built_in">set</span> STRIPE_SECRET <span class="hljs-string">"sk_****"</span>

<span class="hljs-comment"># We can access it on our server-side JS code by:</span>
process.env.STRIPE_SECRET
</code></pre>
<p>For the sake of this tutorial, we will use the <a target="_blank" href="https://stripe.com/docs/payments/accept-a-payment?integration=checkout#set-up-stripe">example API keys</a>, but feel free to use your own. If you use your keys, you will need to add products and prices (<a target="_blank" href="https://support.stripe.com/questions/how-to-create-products-and-prices">documentation</a>).</p>
<h3 id="heading-how-to-add-the-serverless-functions">How to add the serverless functions</h3>
<p>Hang on tight – we are almost there! We only need the server-side code to create Stripe Checkout sessions and complete our demo.</p>
<p>First, to make our function accessible from <a target="_blank" href="https://serverless-payments.netlify.app/api/stripe">https://serverless-payments.netlify.app/api/stripe</a>, we need to add some configurations. Let's start by creating the <code>netlify.toml</code> file, on the root of our repo.</p>
<pre><code class="lang-toml"><span class="hljs-section">[build]</span>
  <span class="hljs-attr">command</span> = <span class="hljs-string">"# no build command"</span>
  <span class="hljs-attr">functions</span> = <span class="hljs-string">"netlify/functions"</span>
  <span class="hljs-attr">publish</span> = <span class="hljs-string">"."</span>

<span class="hljs-section">[[redirects]]</span>
  <span class="hljs-attr">from</span> = <span class="hljs-string">'/api/*'</span>
  <span class="hljs-attr">to</span> = <span class="hljs-string">'/.netlify/functions/:splat'</span>
  <span class="hljs-attr">status</span> = <span class="hljs-number">200</span>
</code></pre>
<p>Then, we can add the session creator function. It’s explained <a target="_blank" href="https://stripe.com/docs/payments/accept-a-payment?integration=checkout#set-up-stripe">here</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// netlify/function/stripe.js</span>

<span class="hljs-keyword">const</span> stripe = <span class="hljs-built_in">require</span>(<span class="hljs-string">"stripe"</span>)(process.env.STRIPE_SECRET);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> stripe.checkout.sessions.create({
    <span class="hljs-attr">payment_method_types</span>: [<span class="hljs-string">"card"</span>],
    <span class="hljs-attr">line_items</span>: [
      {
        <span class="hljs-attr">price_data</span>: {
          <span class="hljs-attr">currency</span>: <span class="hljs-string">"usd"</span>,
          <span class="hljs-attr">product_data</span>: {
            <span class="hljs-attr">name</span>: <span class="hljs-string">"T-shirt"</span>,
          },
          <span class="hljs-attr">unit_amount</span>: <span class="hljs-number">2000</span>,
        },
        <span class="hljs-attr">quantity</span>: <span class="hljs-number">1</span>,
      },
    ],
    <span class="hljs-attr">mode</span>: <span class="hljs-string">"payment"</span>,
    <span class="hljs-attr">success_url</span>: <span class="hljs-string">"https://serverless-payments.netlify.app/success"</span>,
    <span class="hljs-attr">cancel_url</span>: <span class="hljs-string">"https://serverless-payments.netlify.app/cancel"</span>,
  });
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
      <span class="hljs-attr">id</span>: session.id,
    }),
  };
};
</code></pre>
<p>Now we can call the serverless functions from our JS body with <code>fetch("/api/stripe")</code>. It will scale depending on the load and you only paid for the invocations. Then it will be deployed on every push to <code>main</code>. Sweet! 🍬</p>
<p>For the sake of brevity, I skipped the remaining code in the HTML files that handles the Stripe Checkout callbacks. The code is available <a target="_blank" href="https://github.com/aperkaz/serverless-payments">here</a>.</p>
<p>The complete example is available at <a target="_blank" href="https://serverless-payments.netlify.app/">https://serverless-payments.netlify.app</a> . You can test a successful payment flow by using <code>4242 4242 4242 4242</code> as a credit card number.</p>
<p><img src="https://paper-attachments.dropbox.com/s_16ACAF73564EBCEEB7494C6A4225B10D5BBA9580C5BBD5452113AF0E1E7CCE6B_1635620255253_Screenshot+2021-10-30+at+20.57.20.png" alt="Image" width="600" height="400" loading="lazy">
<em>Stripe Checkout in all its glory, accessible from [our page](https://serverless-payments.netlify.app" rel="noreferrer nofollow noopener)</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Online payments are critical to many online businesses but are often implemented in a rush or are over-engineered. The solution presented above applies to single-page applications, so you may not need a fully-fledged server for handling payments just yet. 🙂</p>
<p>I hope this article helps shed some light on adding payment processing to your existing web pages easily. Sell your product quickly and make customers happy!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an SPA with Vue.js and C# Using .NET Core ]]>
                </title>
                <description>
                    <![CDATA[ Let's say you are a front-end developer. Or you have just had to work more with the front end recently. Now and then you have used some back-end technologies, but you've always stayed in your comfort zone, perhaps in the JavaScript world. Or maybe yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-spa-with-vuejs-and-c-using-net-core/</link>
                <guid isPermaLink="false">66d4604a8812486a37369d15</guid>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 15 Sep 2020 19:14:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/dashboard-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let's say you are a front-end developer. Or you have just had to work more with the front end recently.</p>
<p>Now and then you have used some back-end technologies, but you've always stayed in your comfort zone, perhaps in the JavaScript world. Or maybe you have built a small API with Python.</p>
<p>But you have never touched the modern .NET family tech stack.</p>
<p>This tutorial will guide you, step-by-step, in building a modern single page application (SPA) that will take advantage of <a target="_blank" href="https://vuejs.org/">Vue.js</a> for the front-end and <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/core/get-started?tabs=windows">.NET Core (C#)</a> for the back-end.</p>
<p>We will also see how to write some tests, both unit and integration, to cover the front and back end functionality (at least partially).</p>
<p>If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/pizza-app">here</a> ? is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/README.md">README</a> ?.</p>
<h2 id="heading-what-will-we-build">What Will We Build</h2>
<p>?‍? We will build a web app where users can signup/login and just tell us how much they love pizzas, by pressing an "I love it" button.</p>
<p>There are no restrictions on the number of times each user can show us their appreciation. The only requirement is that only logged in users can vote.</p>
<p>On the home page, along with the signup/login buttons, we will put a little bar-chart, displaying the top X users with the highest appreciation (on the X-axis) and the number of votes on the Y-axis.</p>
<h2 id="heading-installation">Installation</h2>
<p>Let's start with the front end. It makes more sense to build the visible parts of our app first. We will build our front end with one of the most famous front-end libraries on the market: Vue.js.</p>
<h3 id="heading-how-to-set-up-the-front-end">How to Set Up the Front End</h3>
<p>In order to install and configure our initial front end setup with Vue.js, we will use the <a target="_blank" href="https://cli.vuejs.org/">Vue CLI</a>. This is a standard command-line tool for developing Vue.js applications.</p>
<p>To install it, use the following command. Note that all commands in this tutorial use <a target="_blank" href="https://www.npmjs.com/">NPM</a>, which you need to have installed on your machine in order to follow along.</p>
<pre><code class="lang-bash">npm install -g @vue/cli
</code></pre>
<p>After successfully installing the Vue CLI, we should be able to start using it to install and configure Vue.js itself. Let's do with the following process.</p>
<p>Step 1: Create a new empty project directory and open it with the following commands:</p>
<pre><code class="lang-python">mkdir pizza-app
cd pizza-app
</code></pre>
<p>Step 2: While in the project root, run the following:</p>
<pre><code class="lang-bash">vue create frontend
</code></pre>
<p>Then from the provided options, select the following:</p>
<ul>
<li>Manual select features</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-52.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js installation - Manually select features</em></p>
<ul>
<li><p>Babel</p>
</li>
<li><p>Router</p>
</li>
<li><p>CSS-Preprocessors (SASS)</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-53.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js installation - select features</em></p>
<p>Then select version 2.x from the next screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-54.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Select version 2.x</em></p>
<p>Next, select 'Use history mode for router?' and 'Sass/SCSS (with node-sass)', like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Linter / Formatter</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Unit Testing with Jest</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>E2E Testing with Cypress</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-60.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After this last step, finish the installation process with the default options.</p>
<p>Now we are ready to run the app by using the following commands from the root project folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm run serve
</code></pre>
<p>After the app is running, you can see it in your browser on http://localhost:8080:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/welcome-to-your-vuejs-app.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js default installation screen</em></p>
<p>Before going on with building the actual components that our front-end app will have, let's install our back-end app via the .NET Core CLI.</p>
<h3 id="heading-how-to-set-up-the-back-end">How to Set Up the Back End</h3>
<p>As mentioned above, we will use another command line tool, the .NET Core CLI, that gives us the ability to create and configure .NET applications.</p>
<p>If you don't have it already, you may use <a target="_blank" href="https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.401-windows-x64-installer">this link</a> to download it and then install it.</p>
<p>Once you have the .NET Core CLI tool installed, go to your project's root directory and run the following command to create our back-end app. Thus we will create a folder called <code>backend</code> and install a .NET web app inside it:</p>
<pre><code class="lang-python">dotnet new web -o backend
</code></pre>
<h3 id="heading-gitignoreio">gitignore.io</h3>
<p>Before continuing with installing the next packages we will need, let's sort out our <em>.gitignore</em> file.</p>
<p>This is a configuration file that tells <a target="_blank" href="https://git-scm.com/">Git</a> what to ignore when committing changes to Git based repositories (the ones in <a target="_blank" href="https://github.com/">GitHub</a>).</p>
<p>Since we want to have one .<em>gitignore</em> file, it will include rules for two types of applications:</p>
<ul>
<li><p>a Node.js based one, which is our Vue.js front end, and</p>
</li>
<li><p>a .NET (C#) one which is our back end.</p>
</li>
</ul>
<p>For this, we will use a tool called <a target="_blank" href="https://www.toptal.com/developers/gitignore"><em>gitignore.io</em></a><em>.</em> This tool will generate such files for us. The advantage of using this tool is that it allows us to type in what our programming languages/platforms are, and it generates the .gitignore file for us.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Use gitignore.io to generate a .gitignore file</em></p>
<p>Also, at the top of the generated file, it saves links for creation or subsequent editing, as shown below.</p>
<pre><code class="lang-python">
<span class="hljs-comment"># Created by https://www.toptal.com/developers/gitignore/api/webstorm,vue,vuejs,visualstudio</span>
<span class="hljs-comment"># Edit at https://www.toptal.com/developers/gitignore?templates=webstorm,vue,vuejs,visualstudio</span>
</code></pre>
<p>Now we are good to go with the rest of the packages we need to install.</p>
<p>First we will install a package called SpaServices, which will allow us to have our app running via only one URL, and pointing to the .NET app. From its side it will proxy requests to our front-end app.</p>
<p>To install it, open your terminal, go to the <code>backend</code> folder of your project, and run the following:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.SpaServices.Extensions --version 3.1.8
</code></pre>
<p>After this, we need to configure our back-end app with the SpaServices package in order to have the desired result.</p>
<p>Every request will go to our .NET back-end app, and if the request is meant for the front end, it will proxy it.</p>
<p>To do this, open the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/Startup.cs">Startup.cs</a> file and update its content to be like this:</p>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Linq;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Builder;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Http;
<span class="hljs-keyword">using</span> Microsoft.Extensions.DependencyInjection;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Configuration;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">backend</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Startup</span>
    {
        <span class="hljs-keyword">public</span> IConfiguration Configuration { <span class="hljs-keyword">get</span>; }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Startup</span>(<span class="hljs-params">IConfiguration configuration</span>)</span>
        {
            Configuration = configuration;
        }

        <span class="hljs-comment">// This method gets called by the runtime. Use this method to add services to the container.</span>
        <span class="hljs-comment">// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)</span>
        {
            <span class="hljs-keyword">string</span> connectionString = Configuration.GetConnectionString(<span class="hljs-string">"DefaultConnection"</span>);
            services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt; options.UseSqlite(connectionString));
            services.AddSpaStaticFiles(configuration: options =&gt; { options.RootPath = <span class="hljs-string">"wwwroot"</span>; });
            services.AddControllers();
            services.AddCors(options =&gt;
            {
                options.AddPolicy(<span class="hljs-string">"VueCorsPolicy"</span>, builder =&gt;
                {
                    builder
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials()
                    .WithOrigins(<span class="hljs-string">"https://localhost:5001"</span>);
                });
            });
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =&gt;
                {
                    options.Authority = Configuration[<span class="hljs-string">"Okta:Authority"</span>];
                    options.Audience = <span class="hljs-string">"api://default"</span>;
                });
            services.AddMvc(option =&gt; option.EnableEndpointRouting = <span class="hljs-literal">false</span>);
        }

        <span class="hljs-comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Configure</span>(<span class="hljs-params">IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext dbContext</span>)</span>
        {
            <span class="hljs-keyword">if</span> (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors(<span class="hljs-string">"VueCorsPolicy"</span>);

            dbContext.Database.EnsureCreated();
            app.UseAuthentication();
            app.UseMvc();
            app.UseRouting();
            app.UseEndpoints(endpoints =&gt; { endpoints.MapControllers(); });
            app.UseSpaStaticFiles();
            app.UseSpa(configuration: builder =&gt;
            {
                <span class="hljs-keyword">if</span> (env.IsDevelopment())
                {
                    builder.UseProxyToSpaDevelopmentServer(<span class="hljs-string">"http://localhost:8080"</span>);
                }
            });
        }
    }
}
</code></pre>
<p>? This is the final version of the Startup.cs file and that's why you probably noticed some more stuff in it. We will get back to this a bit later in this tutorial.</p>
<p>At this point, you should be able to run your back-end app. If you wish to do so, run the following commands from the root folder of your project:</p>
<pre><code class="lang-python">cd backend
dotnet run
</code></pre>
<h2 id="heading-how-to-set-up-authentication">How to Set Up Authentication</h2>
<p>As you may remember from the description at the beginning, our app should have a Sign up/Login option.</p>
<p>In order to fulfill this requirement, we will use a 3rd party service called <a target="_blank" href="https://www.okta.com/">Okta</a>. We will install the necessary packages for using the Okta SDK on both the front end and back end of our app.</p>
<p>But before that, you need to create an account on their <a target="_blank" href="https://developer.okta.com/">website</a> and get access to their admin panel. From there you may create a new application. Here is how it looks on mine:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-62.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now let's start with the package we need for our front end. From the root folder, run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm i @okta/okta-vue
</code></pre>
<p>After this step, we are ready to amend our Vue.js app in order to take advantage of Okta SDK.</p>
<p>We will also install another package, called <a target="_blank" href="https://bootstrap-vue.org/">BootstrapVue</a>, that provides a set of good looking and ready to use Vue.js components. This will help us save development time and will also get us a good looking front end.</p>
<p>To install it, run the following from your <code>frontend</code> folder:</p>
<pre><code class="lang-bash">npm i vue bootstrap-vue bootstrap
</code></pre>
<h3 id="heading-how-to-set-up-the-router">How to Set Up the Router</h3>
<p>It's time to do some coding. We need to edit our <a target="_blank" href="https://router.vuejs.org/">router</a> in order to apply what's coming from Okta authentication services.</p>
<p>You can see the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/router/index.js">full file</a> in my GitHub repo, but here is the essential part that you need to configure with your own details (that you got when you registered on the Okta developer website):</p>
<pre><code class="lang-javascript">Vue.use(Auth, {
  <span class="hljs-attr">issuer</span>: <span class="hljs-string">'https://dev-914982.okta.com/oauth2/default'</span>,
  <span class="hljs-attr">client_id</span>: <span class="hljs-string">'0oatq53f87ByM08MQ4x6'</span>,
  <span class="hljs-attr">redirect_uri</span>: <span class="hljs-string">'https://localhost:5001/implicit/callback'</span>,
  <span class="hljs-attr">scope</span>: <span class="hljs-string">'openid profile email'</span>
})

....

router.beforeEach(Vue.prototype.$auth.authRedirectGuard())
</code></pre>
<h3 id="heading-home-screen">Home screen</h3>
<p>After we get our router sorted, we can finally do some changes to our app's home screen, which will actually be visible to our users.</p>
<p>Open the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/App.vue">App.vue</a> file in your IDE and change its content as follows:</p>
<pre><code class="lang-javascript">&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar</span> <span class="hljs-attr">toggleable</span>=<span class="hljs-string">"md"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"light"</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"light"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-toggle</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"nav-collapse"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-toggle</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-brand</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Love Pizza<span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-brand</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-collapse</span> <span class="hljs-attr">is-nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nav-collapse"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-nav</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">b-nav-item</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"login"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!user"</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">b-nav-item</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">b-nav-item</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"logout"</span> <span class="hljs-attr">v-else</span>&gt;</span>Logout<span class="hljs-tag">&lt;/<span class="hljs-name">b-nav-item</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">b-collapse</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        Love pizza button and clicks counter here
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'app'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">user</span>: <span class="hljs-literal">null</span>
    }
  },
  <span class="hljs-keyword">async</span> created() {
    <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
  },
  <span class="hljs-attr">watch</span>: {
    <span class="hljs-string">'$route'</span>: <span class="hljs-string">'onRouteChange'</span>
  },
  <span class="hljs-attr">methods</span>: {
    login() {
      <span class="hljs-built_in">this</span>.$auth.loginRedirect()
    },
    <span class="hljs-keyword">async</span> onRouteChange() {
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
    },
    <span class="hljs-keyword">async</span> refreshUser() {
      <span class="hljs-built_in">this</span>.user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.$auth.getUser()
    },
    <span class="hljs-keyword">async</span> logout() {
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.$auth.logout()
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
      <span class="hljs-built_in">this</span>.$router.push(<span class="hljs-string">'/'</span>)
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"scss"</span>&gt;</span>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &amp;.router-link-exact-active {
      color: #42b983;
    }
  }
}
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>By now your app might look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-173.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js front-end app - work in progress</em></p>
<p><strong>Note</strong>: Don't be confused by the text 'Pizza button and clicks counter here'. When building UIs, it's a good practice to leave such placeholders for components that are to be developed in the future.</p>
<p>In our case, this is where we will add the components responsible for the 'I love it' button and the counter later. They will show the number of votes per user.</p>
<pre><code class="lang-javascript">&lt;main&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      Love pizza button and clicks counter here
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/main&gt;
</code></pre>
<h3 id="heading-authentication-on-the-back-end">Authentication on the back end</h3>
<p>By now, we have setup our front end to leverage the authentication service provided by Okta. But in order to have the whole picture ready to use, we need to do the same in our back-end app.</p>
<p>This is where we will be doing HTTP calls from, in order to communicate with the database. And, as you will see later, some of these calls will need to be authenticated in order to succeed.</p>
<p>Let's start again with installing some packages that will make our job easier. In your terminal, go to your <code>backend</code> directory and run the following:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 3.1.8
dotnet add package Microsoft.Extensions.Configuration --version 3.1.7
</code></pre>
<p>Then we need another package that will help us set up our database. We will use the SQLite database which is super easy to use in .NET Core setup.</p>
<p>While still in the <code>backend</code> folder, run:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 2.1.1
</code></pre>
<p>Once we're finished with the installations, make sure you got the full content of <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/Startup.cs">Startup.cs</a> file (and if you didn't, do so now).</p>
<h3 id="heading-pizzavotesapiservice">PizzaVotesApiService</h3>
<p>OK everyone, we have set up both our front and back ends to support authentication. We have added SQLite as a database to be used for storing the users votes. And we have an initial version of our home screen.</p>
<p>Now it's time to implement a service on the front end that will allow us to consume our back end's API.</p>
<p>Great job so far! ?</p>
<p>Before being able to make HTTP calls from our front-end app to our back-end app, we need to install one more package in our Vue.js app. It's called <a target="_blank" href="https://www.npmjs.com/package/axios">axios</a> and it gives us the ability to make <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequests</a> from the browser, which is exactly what we need.</p>
<p>Open your terminal, go to the <code>frontend</code> of your project, and run:</p>
<pre><code class="lang-python">npm i axious
</code></pre>
<p>Then, in your IDE, go to the <code>src</code> folder of your front-end app, create a new .js file, and add the following inside it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>

const client = axios.create({
    baseURL: <span class="hljs-string">'http://localhost:5000/api/pizzavotes'</span>,
    json: true
})

export default {
    <span class="hljs-keyword">async</span> execute(method, resource, data) {
        const accessToken = <span class="hljs-keyword">await</span> Vue.prototype.$auth.getAccessToken()
        <span class="hljs-keyword">return</span> client({
            method,
            url: resource,
            data,
            headers: {
                Authorization: `Bearer ${accessToken}`
            }
        }).then(req =&gt; {
            <span class="hljs-keyword">return</span> req.data
        })
    },
    getAll() {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'get'</span>, <span class="hljs-string">'/'</span>)
    },
    getById(id) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'get'</span>, `/${id}`)
    },
    create(data) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'post'</span>, <span class="hljs-string">'/'</span>, data)
    },
    update(id, data) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'put'</span>, `/${id}`, data)
    },
}
</code></pre>
<p>I have named mine <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/PizzaVotesApiService.js">PizzaVotesApiService.js</a>. We will stop with integrating the API for a while and will create another component that will hold the controls the users will use to interact with this API.</p>
<h3 id="heading-dashboard-component">Dashboard Component</h3>
<p>Say hello to our <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/Dashboard.vue">Dashboard.vue</a> component.</p>
<p>This is where we will put the 'I love it' button and a small votes counter.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-63.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>'I love it' button and votes counter</em></p>
<p>We'll also add a nice pizza image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-64.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Pizza image</em></p>
<p>As well as a nice bar chart, showing the top X voters.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Top voters bar chart</em></p>
<p>You may copy and paste the full content of the file from my repo so we can continue with integrating the whole thing.</p>
<h3 id="heading-api-integration">API Integration</h3>
<p>I am going to use a small diagram to depict the data flow. As they say, "a picture is worth a thousand words:"</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/data-flow.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Data flow diagram</em></p>
<p>As you can see (I hope ?) from the diagram, when the user enters their votes, the data goes from the dashboard component through the API service we created for communicating with the back-end API. It then reaches the back-end controller that is actually making the HTTP calls.</p>
<p>Once the data is fetched, the service passes it back to our UI where we show it via our Vue.js components. As you will see, there is some additional logic that checks if the user is authenticated in order to know which calls to execute.</p>
<p>Here is the implementation of the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/PizzaVotesController.cs">controller</a> itself:</p>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Authorization;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">backend.Controllers</span>
{
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
    [<span class="hljs-meta">ApiController</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PizzaVotesController</span> : <span class="hljs-title">ControllerBase</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationDbContext _dbContext;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PizzaVotesController</span>(<span class="hljs-params">ApplicationDbContext dbContext</span>)</span>
        {
            _dbContext = dbContext;
        }

        <span class="hljs-comment">// GET api/pizzavotes</span>
        [<span class="hljs-meta">HttpGet</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;List&lt;PizzaVotes&gt;&gt;&gt; Get()
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.ToListAsync();
        }

        <span class="hljs-comment">// GET api/pizzavotes/{email}</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"{id}"</span>)</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;PizzaVotes&gt;&gt; Get(<span class="hljs-keyword">string</span> id)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.FindAsync(id);
        }

        <span class="hljs-comment">// POST api/pizzavotes</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpPost</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Post</span>(<span class="hljs-params">PizzaVotes model</span>)</span>
        {
            <span class="hljs-keyword">await</span> _dbContext.AddAsync(model);
            <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();
        }

        <span class="hljs-comment">// PUT api/pizzavotes/{email}</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpPut(<span class="hljs-meta-string">"{id}"</span>)</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&gt; <span class="hljs-title">Put</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> id, PizzaVotes model</span>)</span>
        {
            <span class="hljs-keyword">var</span> exists = <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.AnyAsync(f =&gt; f.Id == id);
            <span class="hljs-keyword">if</span> (!exists)
            {
                <span class="hljs-keyword">return</span> NotFound();
            }

            _dbContext.PizzaVotes.Update(model);

            <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();

            <span class="hljs-keyword">return</span> Ok();
        }
    }
}
</code></pre>
<p>Here we have four methods for executing four basic operations:</p>
<ul>
<li><p>get all records from the database</p>
</li>
<li><p>get the records for one user (by their email address)</p>
</li>
<li><p>create a new user record</p>
</li>
<li><p>update an existing user's records.</p>
</li>
</ul>
<p>You have probably noticed the <code>[Authorize]</code> clause above in three of the four methods. Those methods are going to require the user to be authenticated in order to execute.</p>
<p>We will leave the method <code>GET api/pizzavotes</code> for getting all records unauthorized on purpose. Since we would like to show the bar chart on the home screen to all users, we will need to be able to make this call, no matter if the user is authenticated or not.</p>
<h3 id="heading-allow-registration">Allow registration</h3>
<p>Something to note: if you would like to have a 'Sign Up' on your login screen, you need to allow it from the Okta admin panel.</p>
<p>Once logged in, select from the menu <strong>Users-&gt;Registration</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-66.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Allow registration for new users</em></p>
<h3 id="heading-finish-the-back-end">Finish the back end</h3>
<p>In order for your back-end app to become fully functioning, please take a look at <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/tree/master/backend">my repo here</a> and add the missing files.</p>
<p>If you have followed along up to this point you should have the following files (except the <code>test</code> folder, as we are going to add it a bit later):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Back-end app structure</em></p>
<h3 id="heading-finish-the-front-end">Finish the front end</h3>
<p>In order to finalize the work on our front-end app, we will create two more components.</p>
<p>The first one will render the bar chart mentioned above, and the second one will display the email address of the user that is currently logged in.</p>
<p>In your IDE, go to <code>frontend/src/components</code> and create two files, named <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/Username.vue">Username.vue</a> and <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/VotesChart.vue">VotesChart.vue</a>, respectively.</p>
<p>Username.vue is a very short and simple component that takes the user's email address as a prop and renders it. Here is its implementation:</p>
<pre><code class="lang-python">&lt;template&gt;
  &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">username</span>"&gt;{{ <span class="hljs-title">username</span> }}&lt;/<span class="hljs-title">div</span>&gt;
&lt;/<span class="hljs-title">template</span>&gt;

&lt;<span class="hljs-title">script</span>&gt;
<span class="hljs-title">export</span> <span class="hljs-title">default</span> {
  <span class="hljs-title">props</span>:</span> { username: String },
}
&lt;/script&gt;

&lt;style lang=<span class="hljs-string">"scss"</span>&gt;
.username {
  color: rebeccapurple;
  display: flex;
  align-items: center;
  justify-content: center;
}

.username::before {
  content: <span class="hljs-string">"•"</span>;
}

.username::after {
  content: <span class="hljs-string">"•"</span>;
}
&lt;/style&gt;
</code></pre>
<p>The only thing to notice here is that we are using SASS/SCSS for the component styles. That is possible because we chose that option in the beginning (when we were installing our Vue.js app).</p>
<p>For drawing the chart we will use a package called <a target="_blank" href="https://www.npmjs.com/package/vue-chartjs">vue-chartsjs</a>. Install it by running the following command from your <code>frontend</code> folder:</p>
<pre><code class="lang-python">npm i vue-chartjs chart.js
</code></pre>
<p>Our component <strong>VotesChart.vue</strong> is going to be kind of wrapper for the bar chart component that comes from the vue-chartjs package.</p>
<p>We use it for getting the data from the parent component, <strong>Dashboard.vue</strong>, and processing it.</p>
<p>We sort the data array in order to display our top voters, sorted from the largest to the smallest number of votes. Then we pass it to the Bar chart component to visualize it.</p>
<p>Here is the full implementation:</p>
<pre><code class="lang-python">&lt;script&gt;
<span class="hljs-keyword">import</span> { Bar } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-chartjs'</span>

const TOP_N_VOTERS_TO_SHOW_ON_CHART = <span class="hljs-number">10</span>

// Used to sort by votes value - <span class="hljs-keyword">from</span> bigger to smaller (desc)
function sortByStartDate(nextUser, currentUser) {
  const nextVoteValue = nextUser.value
  const currentVoteValue = currentUser.value
  <span class="hljs-keyword">return</span> currentVoteValue - nextVoteValue
}

export default {
  extends: Bar,
  props: { data: Array },

  watch: {
    data: <span class="hljs-keyword">async</span> function (newVal) {
      const sortedVotes = Array.<span class="hljs-keyword">from</span>(newVal).sort(sortByStartDate).slice(<span class="hljs-number">0</span>, TOP_N_VOTERS_TO_SHOW_ON_CHART)
      this.labels = sortedVotes.map(user =&gt; user.id)
      this.values = sortedVotes.map(user =&gt; user.value)

      this.renderChart({
        labels: this.labels,
        datasets: [
          {
            label: <span class="hljs-string">'Pizza Lovers'</span>,
            backgroundColor: <span class="hljs-string">'#f87979'</span>,
            data: this.values,
          }
        ]
      }, {
        scales: {
          yAxes: [{
            ticks: {
              stepSize: <span class="hljs-number">1</span>,
              min: <span class="hljs-number">0</span>,
              max: this.values[<span class="hljs-number">0</span>]
            }
          }]
        }
      })
    }
  }
}
&lt;/script&gt;
</code></pre>
<p>Note that there is a constant at the top of the file which will define how many top voters we would like to show on this chart. Currently it is set to 10, but you can amend it as you like.</p>
<p>Once you are ready with all the front-end stuff and want to run the app, you can do so by:</p>
<p>Going to the <code>frontend</code> folder and running:</p>
<pre><code class="lang-bash">npm run serve
</code></pre>
<p>Going to the <code>backend</code> folder and running:</p>
<pre><code class="lang-bash">dotnet run
</code></pre>
<p>Opening your browser and going to https://localhost:5001.</p>
<h2 id="heading-how-to-test-our-applications">How to test our applications</h2>
<p>So far we have built a modern and fully functioning single page application with a .NET Core back end and a SQLite database. That's a lot of work. Congrats! ✨</p>
<p><em>We easily could stop here and go have some cold</em> ?.</p>
<p>But...</p>
<p>We are reasonable enough to know that if we don't test our apps, we can't guarantee that they will work as expected.</p>
<p>We also know that if we want to make our code testable, we should write in a well structured manner, keeping in mind the <a target="_blank" href="https://www.dotnettricks.com/learn/designpatterns/different-types-of-software-design-principles">main principles of software design</a>.</p>
<p>I hope I've convinced you to continue working through this tutorial. After all, the only thing left for us to do is to write some tests for both of our applications. So let's do it!</p>
<p>We will cover the functioning of our back end API with <strong>integration tests</strong>, and for our front end we will write both <strong>unit</strong> and <strong>integration</strong> tests.</p>
<h3 id="heading-unit-and-integration-tests">Unit and integration tests</h3>
<p>? Before going to the code, I would like to say a few words about these types of tests and answer some of most frequently asked questions.</p>
<p>You might be wondering, what are unit tests? What are integrations tests? Why do we need them? When should we use each of them?</p>
<p>Starting from the first question, a <strong>unit test</strong> is a piece of code that tests the functionality of an encapsulated module (meaning another piece). It's written in as a function or some kind of independent block of code.</p>
<p>They are good to have, because they give you quick development time feedback and keep the code safe from regressions when new features are being added.</p>
<p><strong>Integration tests</strong> are useful when we need to test how multiple modules/units are working together. For example a REST API and a database interaction.</p>
<p>Depending on the size of the application we are working on, we might need only integration tests. But sometimes we need both integration and unit tests to bullet proof our code.</p>
<p>Ideally, we should have them both, as they are essential parts of the testing pyramid and are the cheapest to implement.</p>
<p>But in some cases, like for our back-end app, only integration tests are necessary to cover the functionality that is worth being tested. We have only several API methods for working with the database.</p>
<h3 id="heading-how-to-create-our-back-end-tests">How to Create Our Back End Tests</h3>
<p>This time we will start with creating our test solution. To do so, you need to do a few things.</p>
<p>First, install the xUnit library by running the following from your project root directory:</p>
<pre><code class="lang-bash">dotnet add package xUnit
</code></pre>
<p>Then go to your <code>backend</code> folder and create and empty folder called <code>tests</code> . Then inside that folder run:</p>
<pre><code class="lang-bash">dotnet new xunit -o PizzaVotes.Tests
</code></pre>
<p>Once that's done, open <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/backend.csproj">backend.csproj</a> and add the following two lines to the <code>&lt;PropertyGroup&gt;</code> block:</p>
<pre><code class="lang-python">&lt;GenerateAssemblyInfo&gt;false&lt;/GenerateAssemblyInfo&gt;
&lt;GenerateTargetFrameworkAttribute&gt;false&lt;/GenerateTargetFrameworkAttribute&gt;
</code></pre>
<p>Then go to your <code>tests</code> folder and install the following packages:</p>
<pre><code class="lang-python">Microsoft.AspNetCore.Mvc
Microsoft.AspNetCore.Mvc.Core
Microsoft.AspNetCore.Diagnostics
Microsoft.AspNetCore.TestHost
Microsoft.Extensions.Configuration.Json
Microsoft.AspNetCore.Mvc.Testing
</code></pre>
<p>You do that by executing each of the following commands in your terminal app:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.Mvc --version 2.2.0
dotnet add package Microsoft.AspNetCore.Mvc.Core --version 2.2.5
dotnet add package Microsoft.AspNetCore.Diagnostics --version 2.2.0
dotnet add package Microsoft.AspNetCore.TestHost --version 3.1.8
dotnet add package Microsoft.AspNetCore.Mvc.Testing --version 3.1.8
</code></pre>
<p>After we have everything installed, we are ready to proceed to actually writing some tests.</p>
<p>As you may see here, except the tests themselves, I have added two more files which we need or are good to have when running the tests.</p>
<p>One of them is just a <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/ContentHelper.cs">helper file</a> with one method for dealing with serialized objects and getting the string content. The other is the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/TestFixture.cs">fixture</a> file, where we have configurations and settings for our test server and client.</p>
<p>And, of course, there's <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/PizzaVotesTests.cs">a file</a> with our tests.</p>
<p>I am not going to paste the content of these files here, as this tutorial has become long enough already.</p>
<p><strong>You can just copy them from my repository.</strong></p>
<p>If you take a closer look at the tests, you might notice that we are testing only the first, not authenticated, call for a success response. The rest we are checking only for 401 HTTP response, which is <code>Unautorized</code>.</p>
<p>That's because only the first method is public, that is, it doesn't need authentication.</p>
<p>If we were to have the same tests for all methods, we would have needed to implement a middleware just to authorize our test app in front of Okta's authentication services.</p>
<p>And since the purpose of this tutorial is to learn a variety of things, we might say it's not worth doing.</p>
<p>Now the fun part: how to run the tests. It turns out to be super simple. Just go to your <code>tests</code> directory (where the tests.sln file is) from your terminal and run:</p>
<pre><code class="lang-bash">dotnet <span class="hljs-built_in">test</span>
</code></pre>
<p>You should see something like this in your terminal (ignore the yellow warnings):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-68.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running back-end tests</em></p>
<h3 id="heading-how-to-create-our-front-end-tests">How to Create Our Front-end Tests</h3>
<p>It's time to add some tests to our front end. Here we will do both unit and integration tests.</p>
<p><strong>Unit tests</strong><br>As I mentioned above, unit tests are suitable when we have a module or a component that doesn't have dependencies from the outside world.</p>
<p>Such components turn out to be our Username.vue and VotesChart.vue components.</p>
<p>They are representational components that receive the data they need to function properly via props. This means we can write our tests in the same manner: pass them the data they need and check whether the results of their execution are as expected.</p>
<p>Here's an important thing to mention. It's not that what is provided by the <strong>@vue/test-utils</strong> package (that comes from installing Vue.js), was not enough to test both components.</p>
<p>Rather, for educational purposes, I have decided to install and use the <a target="_blank" href="https://testing-library.com/docs/vue-testing-library/intro">Vue Testing Library</a> as well. That's why one of the components below is tested with @vue/test-utils, but the other is tested with @testing-library/vue.</p>
<p>Don't forget to install it before running the test:</p>
<pre><code class="lang-python">npm i --save-dev @testing-library/vue
</code></pre>
<p>Again, to save space, I am not going to paste the component test's code here, but you can easily see it <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/unit/Username.spec.js">here</a> and <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/unit/VotesChart.spec.js">here</a>.</p>
<p>Then in order to run the unit tests of your front-end app, go to the <code>frontend</code> folder and execute:</p>
<pre><code class="lang-python">npm run test:unit
</code></pre>
<p><strong>Integration test</strong><br>This is probably more interesting for some of you.</p>
<p>If you remember the beginning of this tutorial when we installed our Vue.js app, for our e2e (or integartion) tests solution we selected <a target="_blank" href="https://www.cypress.io/">Cypress.js</a>.</p>
<p>This is a super easy-to-use tool that allows developers to write real e2e tests for their applications by giving them immediate feedback.</p>
<p>From personal experience I would say that working with Cypress is more of a pleasure than with other similar frameworks. If you have previous experience with frameworks like Nightwatch.js or Selenium, what you see below might be familiar to you.</p>
<p>Before running our tests with Cypress, we need to do some minor changes in its configuration.</p>
<p>Find the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/e2e/plugins/index.js">index file</a> under the <code>plugins</code> folder and add the following line to the return statement in the end of the file:</p>
<pre><code class="lang-python">  baseUrl: <span class="hljs-string">"https://localhost:5001"</span>
</code></pre>
<p>Now update the content of the test.js under the <code>specs</code> folder as it's shown <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/e2e/specs/test.js">here</a>.</p>
<p>Once you have it all done, you should be able to run your e2e tests via Cypress. You can do so by executing the following command while you're in your <code>frontend</code> directory:</p>
<pre><code class="lang-python">npm run test:e2e
</code></pre>
<p>⚡Don't forget to start your back-end app before executing the e2e tests so that they work properly.</p>
<p>If you have followed along, after running the command above you should see something like this in your terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-69.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running e2e tests</em></p>
<p>And a new browser window will be opened by Cyrpess.js itself, where you can use the provided UI to see and run your tests.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-70.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cypress.js UI</em></p>
<p>And when all tests pass, you are supposed to see a screen like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-71.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>e2e tests pass successfully</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we have seen how to use one of the hottest technologies on the market, for both the front end and back end.</p>
<p>We've also learned how to combine them properly in order to build a small but fully functioning single page application with database support.</p>
<p>Finally, we also have written unit and integration tests for both ends.</p>
<p>I believe this kind of exercise is beneficial for both experienced and beginner readers, as it covers a lot of different stuff in a step-by-step manner. And you end up with a working example app if you've finished the entire process.</p>
<p>This tutorial ended up being much longer than I initially thought it would be. But if you have done it all, I admire you ?! And I hope it was pleasure for you reading it, as it was for me writing it.</p>
<p>? Thanks for reading! ?</p>
<h2 id="heading-resources">Resources</h2>
<p>You may find below the links that were useful to me in some way while writing this.</p>
<p>https://consultwithgriff.com/spas-with-vuejs-aspnetcore/<br><a target="_blank" href="https://github.com/okta/okta-auth-dotnet">https://github.com/okta/okta-auth-dotnet</a><br><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test">https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test</a><br><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpresponsemessage?view=netcore-3.1">https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpresponsemessage?view=netcore-3.1</a><br><a target="_blank" href="https://vue-test-utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events">https://vue-test-utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events</a><br><a target="_blank" href="https://docs.cypress.io/guides/references/configuration.html#Options">https://docs.cypress.io/guides/references/configuration.html#Options</a><br><a target="_blank" href="https://docs.cypress.io/guides/tooling/visual-testing.html#Functional-vs-visual-testing">https://docs.cypress.io/guides/tooling/visual-testing.html#Functional-vs-visual-testing</a><br><a target="_blank" href="https://www.codingame.com/playgrounds/35462/creating-web-api-in-asp-net-core-2-0/part-3---integration-tests">https://www.codingame.com/playgrounds/35462/creating-web-api-in-asp-net-core-2-0/part-3---integration-tests</a><br><a target="_blank" href="https://testing-library.com/docs/vue-testing-library/intro">https://testing-library.com/docs/vue-testing-library/intro</a><br>https://www.valentinog.com/blog/canvas/</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React Router Tutorial – How to Render, Redirect, Switch, Link, and More, With Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ By Vijit Ail If you have just started with React, you are probably still wrapping your head around the whole Single Page Application concept.  Traditionally routing works like this: let's say you type in /contact in the URL. The browser will make a G... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-router-tutorial/</link>
                <guid isPermaLink="false">66d461773dce891ac3a9683c</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react router ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 26 May 2020 17:55:23 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9ad1740569d1a4ca27f2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Vijit Ail</p>
<p>If you have just started with React, you are probably still wrapping your head around the whole Single Page Application concept. </p>
<p>Traditionally routing works like this: let's say you type in <code>/contact</code> in the URL. The browser will make a GET request to the server, and the server will return an HTML page as the response. </p>
<p>But, with the new Single Page Application paradigm, all the URL requests are served using the client-side code.</p>
<p>Applying this in the context of React, each page will be a React component. React-Router matches the URL and loads up the component for that particular page. </p>
<p>Everything happens so fast, and seamlessly, that the user gets a native app-like experience on the browser. There is no flashy blank page in between route transitions.</p>
<p>In this article, you'll learn how to use React-Router and its components to create a Single Page Application. So open up your favorite text editor, and let's get started.</p>
<h2 id="heading-setup-the-project">Setup the project</h2>
<p>Create a new React project by running the following command. </p>
<pre><code class="lang-sh">yarn create react-app react-router-demo
</code></pre>
<p>I'll be using yarn to install the dependencies, but you can use npm as well.</p>
<p>Next, let's install <code>react-router-dom</code>.</p>
<pre><code>yarn add react-router-dom
</code></pre><p>For styling the components, I'm going to use the Bulma CSS framework. So let's add that as well.</p>
<pre><code>yarn add bulma
</code></pre><p>Next, import <code>bulma.min.css</code> in the <code>index.js</code> file and clean up all the boilerplate code from the <code>App.js</code> file.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">"bulma/css/bulma.min.css"</span>;
</code></pre>
<p>Now that you have the project set up let's start by creating a few page components.</p>
<h2 id="heading-creating-the-page-components">Creating the Page Components</h2>
<p>Create a pages directory inside the src folder where we will park all the page components.</p>
<p>For this demo, create three pages - Home, About, and Profile.</p>
<p>Paste the following inside the Home and About components.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// pages/Home.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> Home = <span class="hljs-function">() =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title is-1"</span>&gt;</span>This is the Home Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida,
      risus at dapibus aliquet, elit quam scelerisque tortor, nec accumsan eros
      nulla interdum justo. Pellentesque dignissim, sapien et congue rutrum,
      lorem tortor dapibus turpis, sit amet vestibulum eros mi et odio.
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Home;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// pages/About.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> About = <span class="hljs-function">() =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title is-1"</span>&gt;</span>This is the About Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
      inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus
      et ultrices posuere cubilia curae; Duis consequat nulla ac ex consequat,
      in efficitur arcu congue. Nam fermentum commodo egestas.
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> About;
</code></pre>
<p>We will create the Profile page later on in the article.</p>
<h2 id="heading-create-the-navbar-component">Create the Navbar Component</h2>
<p>Let's start by creating the navigation bar for our app. This component will make use of the <code>&lt;NavLink /&gt;</code> component from <code>react-router-dom</code>.  </p>
<p>Create a directory called "components" inside the src folder.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/Navbar.js</span>

<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { NavLink } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [isOpen, setOpen] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">return</span> ( 
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar is-primary"</span>
      <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span>
      <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"main navigation"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
          {/* ... */}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  );
 };

 <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Navbar;
</code></pre>
<p>The <code>isOpen</code> state variable will be used to trigger the menu on mobile or tablet devices.</p>
<p>So let's add the hamburger menu.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [isOpen, setOpen] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">return</span> ( 
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar is-primary"</span>
      <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span>
      <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"main navigation"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-brand"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
            <span class="hljs-attr">role</span>=<span class="hljs-string">"button"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">navbar-burger</span> <span class="hljs-attr">burger</span> ${<span class="hljs-attr">isOpen</span> &amp;&amp; "<span class="hljs-attr">is-active</span>"}`}
            <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"menu"</span>
            <span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setOpen(!isOpen)}
          &gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          {/* ... */}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  );
 };
</code></pre>
<p>To add the link in the menu, use the <code>&lt;NavLink /&gt;</code> component by <code>react-router-dom</code>.</p>
<p>The <code>NavLink</code> component provides a declarative way to navigate around the application. It is similar to the <code>Link</code> component, except it can apply an active style to the link if it is active. </p>
<p>To specify which route to navigate to, use the <code>to</code> prop and pass the path name.<br>The <code>activeClassName</code> prop will add an active class to the link if it's currently active.</p>
<pre><code class="lang-jsx">&lt;NavLink
    className=<span class="hljs-string">"navbar-item"</span>
    activeClassName=<span class="hljs-string">"is-active"</span>
    to=<span class="hljs-string">"/"</span>
    exact
&gt;
    Home
&lt;/NavLink&gt;
</code></pre>
<p>On the browser, the <code>NavLink</code> component is rendered as an <code>&lt;a&gt;</code> tag with an <code>href</code> attribute value that was passed in the <code>to</code> prop.</p>
<p>Also, here you need to specify the <code>exact</code> prop so that it is matched precisely with the URL.</p>
<p>Add all the links and finish up the <code>Navbar</code> component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { NavLink } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [isOpen, setOpen] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar is-primary"</span>
      <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span>
      <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"main navigation"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-brand"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
            <span class="hljs-attr">role</span>=<span class="hljs-string">"button"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">navbar-burger</span> <span class="hljs-attr">burger</span> ${<span class="hljs-attr">isOpen</span> &amp;&amp; "<span class="hljs-attr">is-active</span>"}`}
            <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"menu"</span>
            <span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setOpen(!isOpen)}
          &gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">navbar-menu</span> ${<span class="hljs-attr">isOpen</span> &amp;&amp; "<span class="hljs-attr">is-active</span>"}`}&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-start"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-item"</span> <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"is-active"</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>
              Home
            <span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-item"</span>
              <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"is-active"</span>
              <span class="hljs-attr">to</span>=<span class="hljs-string">"/about"</span>
            &gt;</span>
              About
            <span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-item"</span>
              <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"is-active"</span>
              <span class="hljs-attr">to</span>=<span class="hljs-string">"/profile"</span>
            &gt;</span>
              Profile
            <span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-end"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"buttons"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"button is-white"</span>&gt;</span>Log in<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Navbar;
</code></pre>
<p>If you notice here, I've added a Login button. We will come back to the <code>Navbar</code> component again when we discuss protected routes later on in the guide.</p>
<h2 id="heading-rendering-the-pages">Rendering the pages</h2>
<p>Now that the <code>Navbar</code> component is set up let's add that to the page and start with rendering the pages.</p>
<p>Since the navigation bar is a common component across all the pages, instead of calling the component in each page component, it will be a better approach to render the <code>Navbar</code> in a common layout.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// App.js</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mt-2"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">40</span> }}&gt;</span>
        {/* Render the page here */}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<p>Now, add the page components inside of the container.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// App.js</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mt-2"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">40</span> }}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<p>If you check out the results now, you'll notice that both the Home and the About page component gets rendered onto the page. That's because we haven't added any routing logic yet.</p>
<p>You need to import the <code>BrowserRouter</code> component from React Router to add the ability to route the components. All you need to do is wrap all the page components inside of the <code>BrowserRouter</code> component. This will enable all the page components to have the routing logic. Perfect!</p>
<p>But again, nothing's going to change with the results – you are still going to see both the pages rendered. You need to render the page component only if the URL matches a particular path. That's where the <code>Route</code> component from React Router comes into play.</p>
<p>The <code>Router</code> component has a <code>path</code> prop that accepts the page's path, and the page component should be wrapped with the <code>Router</code>, as shown below.</p>
<pre><code class="lang-jsx">&lt;Route path=<span class="hljs-string">"/about"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span></span>
&lt;/Route&gt;
</code></pre>
<p>So let's do the same for the <code>Home</code> component.</p>
<pre><code class="lang-jsx">&lt;Route exact path=<span class="hljs-string">"/"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span></span>
&lt;/Route&gt;
</code></pre>
<p>The <code>exact</code> prop above tells the <code>Router</code> component to match the path exactly. If you don't add the <code>exact</code> prop on the <code>/</code> path, it will match with all the routes starting with a <code>/</code> including <code>/about</code>.</p>
<p>If you go check out the results now, you'll still see both the components rendered. But, if you go to <code>/about</code>, you'll notice that only the <code>About</code> component gets rendered. You see this behavior because the router keeps matching the URL with the routes even after it had matched a route already. </p>
<p>We need to tell the router to stop matching further once it matches a route. This is done using the <code>Switch</code> component from React Router.</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mt-2"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">40</span> }}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Switch</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">About</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Route</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Switch</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
}
</code></pre>
<p>There you go! You have successfully configured routing in your React app. </p>
<h2 id="heading-protected-routes-and-redirect">Protected Routes and Redirect</h2>
<p>When working on Real-world applications, you will have some routes behind an authentication system. You are going to have routes or pages that can only be accessed by a logged-in user. In this section, you'll learn how to go about implementing such routes.</p>
<p><strong><em>Please note</em></strong> <em>that I'm not going to create any login form or have any back-end service authenticate the user. In a real application, you wouldn't implement authentication the way demonstrated here.</em></p>
<p>Let's create the Profile page component that should only be accessed by the authenticated user.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// pages/Profile.js</span>

<span class="hljs-keyword">import</span> { useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Profile = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { name } = useParams();
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title is-1"</span>&gt;</span>This is the Profile Page<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message is-dark"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginTop:</span> <span class="hljs-attr">40</span> }}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message-header"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{name}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message-body"</span>&gt;</span>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.{" "}
          <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Pellentesque risus mi<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>, tempus quis placerat ut, porta
          nec nulla. Vestibulum rhoncus ac ex sit amet fringilla. Nullam gravida
          purus diam, et dictum <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>felis venenatis<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> efficitur. Aenean ac{" "}
          <span class="hljs-tag">&lt;<span class="hljs-name">em</span>&gt;</span>eleifend lacus<span class="hljs-tag">&lt;/<span class="hljs-name">em</span>&gt;</span>, in mollis lectus. Donec sodales, arcu et
          sollicitudin porttitor, tortor urna tempor ligula, id porttitor mi
          magna a neque. Donec dui urna, vehicula et sem eget, facilisis sodales
          sem.
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
</code></pre>
<p>We will grab the user's name from the URL using route parameters.</p>
<p>Add the Profile route into the router.</p>
<pre><code class="lang-jsx">&lt;Route path=<span class="hljs-string">"/profile/:name"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Profile</span> /&gt;</span></span>
&lt;/Route&gt;
</code></pre>
<p>Currently the profile page can be accessed directly. So to make it an authenticated route, create a Higher-Order component (HOC) to wrap the authentication logic.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> withAuth = <span class="hljs-function">(<span class="hljs-params">Component</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> AuthRoute = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> isAuth = !!<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"token"</span>);
    <span class="hljs-comment">// ...</span>
  };

  <span class="hljs-keyword">return</span> AuthRoute;
};
</code></pre>
<p>To determine if a user is authenticated or not, grab the authentication token that is stored in the browser when the user logs in. If the user is not authenticated, redirect the user to the Home page. The <code>Redirect</code> component from React Router can be used to redirect the user to another path.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> withAuth = <span class="hljs-function">(<span class="hljs-params">Component</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> AuthRoute = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> isAuth = !!<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"token"</span>);
    <span class="hljs-keyword">if</span> (isAuth) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Component</span> /&gt;</span></span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Redirect</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span> /&gt;</span></span>;
    }
  };

  <span class="hljs-keyword">return</span> AuthRoute;
};
</code></pre>
<p>You can also pass in other user information like name and user ID using props to the wrapped component.</p>
<p>Next, use the <code>withAuth</code> HOC in the Profile component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> withAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/withAuth"</span>;

<span class="hljs-keyword">const</span> Profile = <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> withAuth(Profile);
</code></pre>
<p>Now, if you try to visit <code>/profile/JohnDoe</code>, you will get redirected to the Home page. That's because the authentication token is not yet set in your browser storage.</p>
<p>Alright, so, let's return to the <code>Navbar</code> component and add the login and logout functionalities. When the user is authenticated, show the Logout button and when the user is not logged in show the Login button. </p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/Navbar.js</span>

<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar is-primary"</span>
          <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span>
          <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"main navigation"</span>
        &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
            {/* ... */}
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-end"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"buttons"</span>&gt;</span>
                {!isAuth ? (
                  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"button is-white"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{loginUser}</span>&gt;</span>
                    Log in
                  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                ) : (
                  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"button is-black"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{logoutUser}</span>&gt;</span>
                    Log out
                  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                )}
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
    );
}
</code></pre>
<p>When the user clicks on the login button, set a dummy token in the local storage, and redirect the user to the profile page. </p>
<p>But we cannot use the Redirect component in this case – we need to redirect the user programmatically. Sensitive tokens used for authentication are usually stored in cookies for security reasons.</p>
<p>React Router has a <code>withRouter</code> HOC that injects the <code>history</code> object in the props of the component to leverage the History API. It also passes the updated <code>match</code> and <code>location</code> props to the wrapped component.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// components/Navbar.js</span>

<span class="hljs-keyword">import</span> { NavLink, withRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">(<span class="hljs-params">{ history }</span>) =&gt;</span> { 
  <span class="hljs-keyword">const</span> isAuth = !!<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"token"</span>);

  <span class="hljs-keyword">const</span> loginUser = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"token"</span>, <span class="hljs-string">"some-login-token"</span>);
    history.push(<span class="hljs-string">"/profile/Vijit"</span>);
  };

  <span class="hljs-keyword">const</span> logoutUser = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">"token"</span>);
    history.push(<span class="hljs-string">"/"</span>);
  };

  <span class="hljs-keyword">return</span> ( 
   {<span class="hljs-comment">/* ... */</span>}
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> withRouter(Navbar);
</code></pre>
<p>And <em>voilà</em>! That's it. You have conquered the land of authenticated routes as well.</p>
<p>Check out the live demo <a target="_blank" href="https://react-router-demo.vijitail.dev/">here</a> and the complete code in this <a target="_blank" href="https://github.com/vijitail/react-router-demo">repo</a> for your reference.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope by now you have a fair idea of how client-side routing works in general and how to implement routing in React using the React Router library. </p>
<p>In this guide, you learned about the vital components in React Router like <code>Route</code>, <code>withRouter</code>, <code>Link</code>, and so on, along with some advanced concepts like authenticated routes, that are required to build an application. </p>
<p>Do check out the React Router <a target="_blank" href="https://reacttraining.com/react-router/web/guides/quick-start">docs</a> to get a more detailed overview of each of the components.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Want to understand the MEAN Stack quickly? Here's documentation with useful diagrams. ]]>
                </title>
                <description>
                    <![CDATA[ By Clark Jason Ngo This article is based on my capstone for the City University of Seattle. The title of my research is "Software Documentation and Architectural Analysis of Full Stack Development". The goal of my research was to reduce the learning ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/cjn-understanding-mean-stack-through-diagrams/</link>
                <guid isPermaLink="false">66d45e20230dff01669057d9</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sun, 08 Mar 2020 11:01:56 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/Screen-Shot-2020-03-08-at-3.58.32-AM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Clark Jason Ngo</p>
<p>This article is based on my capstone for the <a target="_blank" href="https://www.cityu.edu/">City University of Seattle</a>. The title of my research is "Software Documentation and Architectural Analysis of Full Stack Development". The goal of my research was to reduce the learning curve in understanding open source projects and full stack development, and I choose the MEAN Stack. </p>
<p>I have created the following diagrams, using <a target="_blank" href="https://www.lucidchart.com/">Lucidchart</a>, for easier comprehension. These UML diagrams were based on the 4+1 architectural view model:</p>
<ul>
<li>Restaurant Analogy</li>
<li>Process View using Sequence Diagram</li>
<li>Scenario using Sequence Diagram</li>
<li>Physical View using Deployment Diagram</li>
<li>Development View using Package Diagram</li>
<li>Logical View using Class Diagram</li>
</ul>
<p>The research was more focused on Deployment and Request and Response Flow.</p>
<h1 id="heading-mean-stack">MEAN Stack</h1>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/logo1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The MEAN Stack is a full-stack JavaScript open-source solution. It's made up of MongoDB, Express, Angular, and Node.js. </p>
<p>The idea behind it is to solve the common issues with connecting those frameworks, build a robust framework to support daily development needs, and help developers use better practices while working with popular JavaScript components.</p>
<h2 id="heading-back-end-with-nodejs">Back-end with Node.js</h2>
<p>Node.js is built for handling asynchronous I/O while JavaScript has an event loop built-in for the client-side. This makes Node.js fast compared to other environments. However, the event-driven/callback approach makes Node.js difficult to learn and debug.</p>
<p>Node.js includes modules such as Mongoose, which is a MongoDB object modeling, and the Express web application framework. Through Node modules, abstraction can be achieved, which reduces the overall complexity of the MEAN stack.</p>
<h2 id="heading-back-end-with-express-framework">Back-end with Express Framework</h2>
<p>Express is a minimalist and unopinionated application framework for Node.js. It is a layer on top of Node.js that is feature-rich for web and mobile development without hiding any Node.js functionality.</p>
<h2 id="heading-front-end-with-angular">Front-end with Angular</h2>
<p>Angular is a web development platform built in TypeScript that provides developers with robust tools for creating the client side of web applications. </p>
<p>It allows development of single-page web applications where content changes dynamically based on user behavior and preferences. It features dependency injection to ensure that whenever a component is changed, other components related to it will be changed automatically.</p>
<h2 id="heading-database-with-mongodb">Database with MongoDB</h2>
<p>MongoDB is a NoSQL database which stores data in BJSON (Binary JavaScript Object Notation). </p>
<p>MongoDB became the de facto standard database for Node.js applications to fulfill the "JavaScript everywhere" paradigm using JSON (JavaScript Object Notation) to transmit data across different tiers (front-end, back-end, and the database).</p>
<p>Now that we've gotten those basics out of the way, let's look at these diagrams.</p>
<h2 id="heading-restaurant-analogy">Restaurant Analogy</h2>
<p>As I wanted to tackle the steep learning curve, I chose a restaurant analogy to let the user understand and retain the process for request and response in a full stack application.</p>
<p>The customer (end-user) requests their order through the waiter (controller), and the waiter hands over the request to the person at the order window (service factory). </p>
<p>These three components makes up the front-end server. The service factory will be the one to communicate with the cook (controller) in the back-end. The cook will then grab the necessary ingredients (data) in the fridge (database server).</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/analogy_request.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The fridge will be able to provide the necessary material (data) to the cook in the back-end. The cook can now process that data and send back to the service factory of the front-end. </p>
<p>The controller (waiter) will hand-over the prepared meal to the customer (user). The customer will now be able to consume the meal (data).</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/analogy_response.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-process-view-using-sequence-diagram">Process View using Sequence Diagram</h2>
<p>Who uses it or what it shows:</p>
<ul>
<li>Integrators</li>
<li>Performance</li>
<li>Scalability</li>
</ul>
<p>In the process view, I show the front-end server and back-end server separately at first and then connect them together with the database server. </p>
<p>In the first example, an Angular application was deployed with hard-coded JSON in a <code>service.ts</code> file (located in the Service Factory).</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/pro_frontend.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The Angular application can communicate with third-party APIs to obtain data and display it to the user.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/pro_frontend_api.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In our back-end, the Node.js application example starts with a hard-coded JSON that can be processed and used as a response.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/pro_backend.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This back-end can be connected to third-party APIs or a database server to obtain the JSON, process it, and send it back to the requester.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/pro_backend_database.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With the front-end server, back-end server, and database server processes explained, I show the combination of these three servers below:</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/pro_mean.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When an HTTP request is made, the front-end will be triggered and Angular will pick up the request. The request will be passed internally in Angular with Route sending a request for the view to View/Template. </p>
<p>View/Template will request the Controller. The Controller will then create an HTTP request to a RESTful (Representational state transfer) endpoint to the Server Side, which is Express/Node.js. The request will then be passed internally with Express/Node.js from its Route to the Controller/Model. </p>
<p>The Controller/Model will make a request through the Mongoose ODM to interact with the Database Server that has MongoDB. MongoDB will process the request and respond the callback to Express/Node.js. </p>
<p>Express/Node.js sends a JSON response to the Angular Controller. The Angular Controller would then respond with a view.</p>
<h2 id="heading-scenario-view-using-sequence-diagram">Scenario View using Sequence Diagram</h2>
<p>Who uses it or what it shows:</p>
<ul>
<li>Describe interactions between objects and between processes</li>
</ul>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/sce_book_store.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The scenario described above involves a user accessing a book store application. When the user enters the URL, JavaScript will be run and will hit the router of the front-end server, which is the AppRoutingModule. The AppRoutingModule will call the BooksComponent, which will load fetchBooks as its dependency injection. </p>
<p>fetchBooks will then create an HTTP request to the back-end server that has a router, controller, and model to process the request and query the database server. </p>
<p>The database server processes the query, and with the back-end server waiting, will grab the data and sent it back to the front-end server as a JSON response. </p>
<p>The front-end will now have the data and the template view to show to the user.</p>
<h2 id="heading-physical-view-using-deployment-diagram">Physical View using Deployment Diagram</h2>
<p>Who uses it and what it shows:</p>
<ul>
<li>System engineer</li>
<li>Topology</li>
<li>Communications</li>
</ul>
<p>The deployment diagram shows 3 servers: front-end, back-end, and database. In the front-end, we require the browser as Angular applications are browser-based web applications.</p>
<p>The back-end server hosts our Node.js with Express on top of Node.js. In Express, we have the application and Mongoose on top of it. Express will handle the communication on both front-end and database. The database server only includes MongoDB. It uses JSON to communicate across servers.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/phy_overview.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In our first build of the MEAN Stack, we’ll be deploying locally using our local machine (localhost) to deploy the front-end server, back-end server, and database server. </p>
<p>We’ll be using the following default ports: Angular - port 4200, Node.js/Express – port 3000, and MongoDB – port 27017.</p>
<p>The diagram below shows the full stack web application in UML notation.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/phy_local_uml.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Moving further to actual production, the thing to migrate to the cloud is our database. For MongoDB, I chose MongoDB Atlas as the cloud solution.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/phy_local_cloud_uml.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The last step to production deployment is uploading our front-end code to Amazon S3 and uploading the back-end in an EC2 instance with AWS. They would all communicate to each other with HTTP / HTTPS endpoints.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/phy_cloud_uml.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here's another diagram to show our production deployment without using UML notation.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/phy_cloud.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-development-view-using-package-diagram">Development View using Package Diagram</h2>
<p>Who uses it and what it shows:</p>
<ul>
<li>Programmers</li>
<li>Software Management</li>
</ul>
<p>The package view of the Angular application shows that every Angular Component is imported in the AppModule. AppModule and AppRoutingModule are dependent on BooksComponent. The BooksComponent is dependent on BookDetailComponentDialog and ApiService.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/dev_angular.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The package view of the Node.js application shows that all CRUD operations (controllers) such as fetch all books, fetch a book, update a book, and delete a book are imported by the app. Also, all the CRUD operations logic resides in the model book.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/dev_nodejs.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-logical-view-using-class-diagram">Logical View using Class Diagram</h2>
<p>Who uses it and what it shows:</p>
<ul>
<li>End-user</li>
<li>Functionality</li>
</ul>
<p>The book store application only showcased a single class called Book. The class members are: title, isbn, author, picture and price. The methods are: addBook, fetchBooks, fetchBook, updateBook, and deleteBook.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/log_book.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The model Book’s structure in JSON format.</p>
<p><img src="https://github.com/clarkngo/cityu_capstone/raw/master/images/log_book_json.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Here are some videos for the diagrams:</strong></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/hPNziXpjf7E" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p><strong>Documentation available on my GitHub:</strong></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/clarkngo/cityu_capstone">https://github.com/clarkngo/cityu_capstone</a></div>
<p>Find me on <a target="_blank" href="https://www.linkedin.com/in/clarkngo/">LinkedIn</a>. =)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Top JavaScript Frameworks For Front-End Development in 2020 ]]>
                </title>
                <description>
                    <![CDATA[ By Shifa Martin Front-end developers might know this game already: you type “top JavaScript frameworks” into Google and you get so many JavaScript frameworks from which to choose. There are always more choices for JavaScript frameworks. And it's alwa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/complete-guide-for-front-end-developers-javascript-frameworks-2019/</link>
                <guid isPermaLink="false">66d460f03bc3ab877dae2230</guid>
                
                    <category>
                        <![CDATA[ Angular ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Angular ]]>
                    </category>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vuex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Applications ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 08 Nov 2019 12:45:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/11/1181834_8257_2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Shifa Martin</p>
<p>Front-end developers might know this game already: you type “top JavaScript frameworks” into Google and you get so many JavaScript frameworks from which to choose.</p>
<p>There are always more choices for JavaScript frameworks. And it's always tough to choose a JavaScript framework for front-end development.</p>
<p>So, what are front-end developers looking for in their tech stacks? As a full-time developer, I know it comes down to rapid development and easy-to-make UIs.</p>
<p>Rather than try to be decisive, we, 450+ developers at <a target="_blank" href="https://www.valuecoders.com/">ValueCoders software development company</a>, voted and shortlisted a few of the best JavaScript frameworks.</p>
<h3 id="heading-our-vote-goes-to-react">Our Vote Goes to React</h3>
<p>I was not surprised to see this. Most of our developers voted for React as one of the best JavaScript frameworks. There have been plenty of projects along the way that our front-end developers are handling that highlighted the strengths of the JS framework. React provides a combination of the following:</p>
<ul>
<li>Reusable components</li>
<li>Synchronization of state and view</li>
<li>Routing and template system</li>
</ul>
<p>Our developers implement front-end logic by relying heavily on React. At the same time, I was surprised by how simple it was to create  applications with React.</p>
<h3 id="heading-here-is-an-overview-of-our-app">Here is an Overview of Our App</h3>
<p>The application is simple. It’s a <a target="_blank" href="https://www.valuecoders.com/case-studies/online-music-school">studio management app</a> for music teachers that helps them focus more on their teaching and less on the management of their music studio.</p>
<p>The key challenge was creating one ‘Activity Dashboard’ for teachers where they could manage all their students' activities and track their progress over time. We overcame this challenge by using <a target="_blank" href="https://redux.js.org/introduction/ecosystem">Redux libraries</a> to build the platform. We built a teacher’s studio from where they could manage their students' progress, showcase new music lessons, chat with them, compare students music playing with live music, and provide them feedback.</p>
<p><img src="https://lh3.googleusercontent.com/MlqY_vfvZEVG1w6UKUhasIekmnODKhSBr5MM3WVLVSzS6_rXyi-wq0npVhBqILg1Totwwccloq9XWQwFF-VWpOCtF8vqj7yOmV4tDKJ9vyNM8jg_YJYvt4x6njz51w1-eI9ojzQ6" alt="Image" width="315" height="564" loading="lazy"></p>
<p>So, this is my experience with React JS. But many would argue that Vue is one of the best front-end JavaScript frameworks with many useful tools. </p>
<p>Front-end developers are the ones deciding which JavaScript framework will do the job. In doing so, they face a lot of challenges because they need to decide what they’ve always needed. Often, we have to choose a JavaScript framework now, not after a week of research. In that case, most developers go with what they know. But maybe the stacks you’re familiar with are no longer cutting it in terms of performance.</p>
<p>Even just choosing among Angular, React, an Vue, it is difficult for new developers. Rather than making it more exhaustive for you, here is the list of the top JavaScript frameworks for front-end developers.</p>
<h2 id="heading-the-big-5-javascript-frameworks">The Big 5 JavaScript Frameworks</h2>
<p>The five JavaScript frameworks that currently dominate the market in terms of popularity and usage are:</p>
<ul>
<li>React </li>
<li>Vue </li>
<li>Angular </li>
<li>Ember</li>
<li>Backbone.js. </li>
</ul>
<p>They each have large communities. If you are a front-end developer or are going to start your new project on front-end technologies, these five are your best bets. Here’s a look at the npm trends over the last six months.</p>
<p><img src="https://lh3.googleusercontent.com/XxQMWDLC6oyQMOna5NKP_lDYYgmXgHnLclulqOZ-0C-l5n-hFw-q5BLnbM7hRGEB8kl9w7GSUAzf6K1gexsZrcp9O7T7ju_ns94qNUR_2-12cR2_Rn30F3f9ul4iO2rGqdRv1SYl" alt="Image" width="1080" height="450" loading="lazy"></p>
<p><img src="https://lh3.googleusercontent.com/cSAcJvog37qO1rB1OCFvKPouN6R5OcbHam-Qmzc6Ei-nnOLxr-FoeeHD6sKQKEd-b3vmushcuEFjN02EPKbzYkEuOQlHtJizRRS2zVD77TUKRM-leOcGJpa1ZZqzQUAaD3EdUvRM" alt="Image" width="1098" height="332" loading="lazy"></p>
<h2 id="heading-1-react">1. React</h2>
<p>React is the definite leader in the JS world. This JavaScript framework uses a <a target="_blank" href="https://en.wikipedia.org/wiki/Reactive_programming">reactive approach</a> and also introduces many of its own concepts for front-end web development. </p>
<p>To use React , you’ll have to learn to use a plethora of additional tools to reach high flexibility in front-end development. For example, here's a less exhaustive list of libraries you can use with React: Redux, MobX, Fluxy, Fluxible, or RefluxJS. React can also be used with jQuery AJAX, fetch API, Superagent, and Axios.</p>
<h3 id="heading-concurrent-mode">Concurrent Mode</h3>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/reactjs/status/1187411505001746432"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>React is constantly working towards improving concurrent mode. To take this forward, React Conf 2019 wrapped up last month where the React team talked about improving <a target="_blank" href="https://reactjs.org/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense.html">Concurrent Mode and the Suspense model</a>. Both the features make React apps more responsive by rendering trees without blocking threads. This allows React to focus on high priority tasks like responding to user input.</p>
<h3 id="heading-suspense">Suspense</h3>
<p>React also introduced Suspense to improve the developer’s experience when handling asynchronous data fetching in React apps. In short, the new update to Suspense lets the component wait until a condition is met. </p>
<p>Hooks are another important update to React 16.8. <a target="_blank" href="https://reactjs.org/docs/hooks-overview.html">React hooks</a> lets you use every important feature of React – server-side rendering, accessibility, concurrent mode, and suspense – all without writing a class.</p>
<p>React applications are divided into multiple components that contain both business logic and HTML markup functions. To improve the communication between components, developers can use either Flux or a similar JavaScript library. </p>
<p>React also introduced objects, like state and props. With the state and props objects, you can simply pass data from a component to the layout or from a parent component to a child component.</p>
<p><strong>Introduction to the React ecosystem:</strong></p>
<ul>
<li>The React library plus React router for implementing routes.</li>
<li><a target="_blank" href="https://reactjs.org/docs/react-dom.html">React-DOM</a> for DOM manipulation.</li>
<li>React developer tools for Firefox and Chrome browsers.</li>
<li><a target="_blank" href="https://reactjs.org/docs/introducing-jsx.html">React JSX</a>, a markup language that mixes HTML into JavaScript.</li>
<li>React Create App command line interface to set up a React project.</li>
<li>Redux and Axios libraries to organize communication with the backend team.</li>
</ul>
<p>No doubt, React is one of the most popular JavaScript frameworks. And, I think that React can be your first choice for creating advanced-grade apps. </p>
<h2 id="heading-2-angular-2-to-angular-9">2. Angular 2 to Angular 9</h2>
<p>Angular 9 will mark a turning point revealed by the Angular team at the recent AngularConnect 2019. According to the update, the team is planning to make the <a target="_blank" href="https://angular.io/guide/ivy">Angular Ivy compiler</a> available for all apps. The main benefit of Angular Ivy is that it is able to reduce the size of applications. </p>
<p>Angular today has become very advanced and modular to use for front-end development. Previously you could insert a link to the AngularJS library in the main HTML file, but now you can do the same by installing separate modules. </p>
<p>Angular's flexibility is commendable. That’s why Angular's 1.x versions are still in demand. However, many developers currently rely on Angular 2+ because of its MVC architecture which has changed substantially to a component based architecture.  </p>
<p>Angular has a couple of additional challenges. You're almost obliged to use TypeScript to ensure type safety in Angular apps. TypeScript makes the Angular 2+ framework not so pleasant to work with. </p>
<p><strong>Angular’s ecosystem is comprised of:</strong></p>
<ul>
<li>For quick project setup, Angular's command line interface is helpful.</li>
<li>Developers will get a set of modules for Angular projects: @angular/common, @angular/compiler, @angular/core, @angular/forms, @angular/http, @angular/platform-browser, @angular/platform-browser-dynamic, @angular/router, and @angular/upgrade. </li>
<li>Angular uses Zone.js a JavaScript library to implement zones in Angular apps.</li>
<li>TypeScript and CoffeeScript both can be used with Angular.</li>
<li>For communication with server-side apps, Angular uses RxJS and the Observable pattern.</li>
<li>Angular Augury for <em>debugging</em> Angular apps.</li>
<li>Angular Universal for creating server-side apps with Angular.</li>
</ul>
<p>Angular2 is a complete JavaScript framework with all the tools a modern front-end developer needs. You can choose Angular if you don’t like to work with additional libraries as with React.</p>
<h2 id="heading-3-vue">3. Vue</h2>
<p>The Snyk JavaScript framework report for 2019 is out. The report mainly focused on security risks in both React and Angular.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/snyksec/status/1189527376197246977"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>The concept of Vue has been taken from Angular and React, but Vue is better in many ways. I’ll talk about its features, but first check out what the <a target="_blank" href="https://snyk.io/blog/javascript-frameworks-security-report-2019/">Synk report</a> says about Vue's front-end security. Vue has been downloaded 40 million times this year and records only <a target="_blank" href="https://snyk.io/vuln/npm:vue">four direct vulnerabilities</a>. All of them have been fixed. </p>
<p>For any front-end developer unfamiliar with Vue, let’s clarify several points. </p>
<p>With Vue you store component logic and layouts along with stylesheets in one file. This is the same way React works, without stylesheets. To let components talk to each other, Vue uses the props and state objects. This approach also existed in React before Vue adopted it.</p>
<p>Similar to Angular, Vue wants you to mix HTML layouts with JavaScript. You have to use Vue directives such as v-bind or v-if to interpolate values from the component logic to templates.</p>
<p>One of the reasons why Vue is worth considering instead of React is because of the Redux library that’s often used in large-scale React applications. As explained in the React section, when a React+Redux app grows bigger, you’ll spend a lot of time applying small changes to multiple files instead of actually working on features. The Vuex library – a Flux-like state management tool designed for Vue – seems less unwieldy than Redux.</p>
<p>If you're choosing between Vue and Angular, the reasons to opt for Vue over Angular can be reduced to the following: Angular is an over-complicated, full-fledged framework with a restrictive nature; Vue is much simpler and less restrictive than Angular.</p>
<p>Another advantage of Vue over Angular and React is that you don’t have to learn JavaScript once more. </p>
<p><strong>An introduction to the VueJS ecosystem:</strong></p>
<ul>
<li>Vuex comes with a dedicated library for application management.</li>
<li>Vuex is similar to the concept of Flux.</li>
<li>You will get Vue-loader for components and vue.js devtools for Chrome and Firefox browsers.</li>
<li>Vue-resource and Axios tools for communication between Vue and the backend source.</li>
<li>Vue.js support Nuxt.js for creating server-side applications with Vue; Nuxt.js is basically a competitor to Angular Universal.</li>
<li>You will get a Weex JavaScript library with Vue syntax that is used for mobile app development.</li>
</ul>
<p>Vue is excellent in terms of its workflow to other frameworks. I might opt for Vue because it’s less complicated than React and Angular JS and a great choice for developing enterprise-level apps.</p>
<h2 id="heading-4-ember">4. Ember</h2>
<p>Ember 3.13 released this year with some new updates and features. Ember is just like Backbone and AngularJS, and is also one of the oldest JavaScript frameworks. But with the new update, <a target="_blank" href="https://blog.emberjs.com/2019/09/25/ember-3-13-released.html">Ember 3.13</a> is compatible with new bug fixes, performance improvements, and deprecation. Tracked property updates have also been introduced that allow simpler ways of tracking state change in the ergonomic system of Ember apps.</p>
<p>Ember has a relatively intricate architecture, which will allow you to quickly build huge client-side applications. It realizes a typical MVC JavaScript framework, and Ember’s architecture comprises the following parts: adapters, components, controllers, helpers, models, routes, services, templates, utils, and addons.</p>
<p>One of Ember’s best features is its command line interface tool. The Ember CLI helps front-end developers be highly productive and lets them complete projects on time. You can not only create new projects with ready setups, but you can also create controllers, components, and project files using automatic generation. </p>
<p><strong>The EmberJS ecosystem is comprised of:</strong></p>
<ul>
<li>Ember CLI tool for quick prototyping and managing dependencies.</li>
<li>Ember server built into the framework for the development of apps.</li>
<li>You'll get Ember.js library and Ember Data for data management.</li>
<li>Handlebars template engine for Ember applications.</li>
<li>QUnit testing framework for Ember.</li>
<li>Ember Inspector development tool for Chrome and Firefox browsers.</li>
<li>Ember Observer for public storage and Ember addons to implement generic functionalities.</li>
</ul>
<p>Although Ember is underrated, it's perfect for creating complex client-side apps. </p>
<h2 id="heading-5-backbonejs">5. Backbone.js</h2>
<p>Backbone is a JavaScript framework based on the MVC architecture. In Backbone.js, the View of MVC helps implement component logic similarly to a Controller. Backbone view can use engines like Mustache and Underscore.js.</p>
<p>Backbone is an easy to use JavaScript framework that allows for quick development of single page applications. To use Backbone.js to the fullest extent, you’ll have to choose tools: Chaplin, Marionette, Thorax, Handlebars or Mustache, and so on. </p>
<p>If you need to design an app that has different types of users, Backbone collections (arrays) can be used here to separate the models. Backbone.Events can be used with Backbone models, collections, routes, and views. </p>
<p><strong>Introducing the BackboneJS ecosystem:</strong></p>
<ul>
<li>The Backbone library consists of events, models, collections, views, and router.</li>
<li>Underscore.js, a JavaScript library with helper functions that you can use to write cross-browser JavaScript.</li>
<li>You can use template engines such as Mustache and jQuery-tmpl.</li>
<li><a target="_blank" href="http://backplug.io/">BackPlug</a> online repository with a lot of ready solutions for Backbone-based apps.</li>
<li>Backbone generator CLI for building Backbone apps.</li>
<li>Marionette, Thorax, and Chaplin JavaScript libraries to develop an advanced architecture for Backbone apps.</li>
</ul>
<p>Backbone.js is a perfect choice for front-end and back-end development as it supports REST APIs that are used to synchronize the front-end and back-end. </p>
<h2 id="heading-need-more-help">Need More Help?</h2>
<p>All front-end developers out there, if you need help with JavaScript frameworks, feel free to <a target="_blank" href="https://www.valuecoders.com/contact">get in touch</a>. Or, you can also contact us to <a target="_blank" href="https://www.valuecoders.com/hire-developers/hire-reactjs-developers">hire ReactJS developers</a>, Vue developers, or Angular developers. </p>
<blockquote>
<p>_Or, leave me a note or <a target="_blank" href="https://twitter.com/ValueCoders?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor">Tweet</a> out to me._</p>
</blockquote>
<p>Remember this article gives you a general roadmap on JavaScript frameworks. Tell me if I have missed something, and we can discuss that. I hope it will also help achieve your front-end development goals.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a Vue.js app using Single-File Components, without the CLI. ]]>
                </title>
                <description>
                    <![CDATA[ An understanding of Vue’s single-file components (SFCs) and Node Package Manager (NPM) will be helpful for this article. A framework’s command line interface, or CLI, is the preferred method to scaffo ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-vue-js-app-using-single-file-components-without-the-cli-7e73e5b8244f/</link>
                <guid isPermaLink="false">66d46177d1ffc3d3eb89de7e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webpack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Brandon Wozniewicz ]]>
                </dc:creator>
                <pubDate>Tue, 04 Dec 2018 20:48:42 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*L2343t5yIriMN69KY6jWEw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><em>An understanding of Vue’s single-file components (SFCs) and Node Package Manager (NPM) will be helpful for this article.</em></p>
<p>A framework’s command line interface, or CLI, is the preferred method to scaffold a project. It provides a starting point of files, folders, and configuration. This scaffolding also provides a development and build process. A development process provides a way to see updates occurring as you edit your project. The build process creates the final version of files to be used in production.</p>
<p>Installing and running Vue.js (“Vue”) can be done with a script tag that points to the Vue content delivery network (CDN). No build or development process is necessary. But, if you use Vue single-file components (SFCs), you need to convert those files into something the browser can understand. The files need to be converted to Hyper-Text Markup Language (HTML), Cascading Style Sheets (CSS), and JavaScript (JS). In this case, a development and build process must be used.</p>
<p>Instead of relying on the Vue CLI to scaffold our project and provide us with a development and build process, we will build a project from scratch. We will create our own development and build process using Webpack.</p>
<h4 id="heading-what-is-webpack">What is Webpack?</h4>
<p>Webpack is a module bundler. It merges code from multiple files into one. Before Webpack, the user included a script tag for each JavaScript file. Although browsers are <em>slowly</em> supporting ES6 modules, Webpack continues to be the preferred way to build modular code.</p>
<p>Besides being a module bundler, Webpack can also transform code. For example, Webpack can take modern JavaScript (ECMAScript 6+) and convert it into ECMAScript 5. While Webpack <em>bundles</em> the code itself, it transforms the code with loaders and plugins. Think of loaders and plugins as add-ons for Webpack.</p>
<h4 id="heading-webpack-and-vue">Webpack and Vue</h4>
<p>Single-file components allow us to build an entire component (structure, style, and function) in one file. And, most code editors provide syntax highlighting and linting for these SFCs.</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*anBAz9QzClNtmAxp4ujdGA.png" alt="Image" width="800" height="879" loading="lazy">

<p>_[Vue Single File Component](<a href="https://vuejs.org/v2/guide/single-file-components.html">https://vuejs.org/v2/guide/single-file-components.html</a>" rel="noopener" target="<em>blank" title="): notice the .vue extension.</em></p>
<p><em>Notice the file ends with .vue. The browser doesn’t know what to do with that extension. Webpack, through the use of loaders and plugins, transforms this file into the HTML, CSS, and JS the browser can consume.</em></p>
<h3 id="heading-the-project-build-a-hello-world-vue-application-using-single-file-components">The Project: Build a Hello World Vue Application Using Single-File Components.</h3>
<h4 id="heading-step-1-create-the-project-structure">Step 1: Create the project structure</h4>
<p>The most basic Vue project will include an HTML, JavaScript, and a Vue file (the file ending in <em>.vue</em>). We will place these files in a <strong>folder called</strong> <code>src</code><strong>.</strong> The source folder will help us separate the code we are writing from the code Webpack will eventually build.</p>
<p>Since we will be using Webpack, we need <strong>a Webpack configuration file.</strong></p>
<p>Additionally, we will use a compiler called Babel. Babel allows us to write ES6 code which it then compiles into ES5. Babel is one of those “add-on features” for Webpack. <strong>Babel also needs a configuration file.</strong></p>
<p>Finally, since we are using NPM, we will also have <strong>a node_modules folder</strong> and <strong>a package.json file.</strong> Those will be created automatically when we initialize our project as an NPM project and begin installing our dependencies.</p>
<p>To get started, create a folder called <code>hello-world</code>. From the command line, change to that directory and run <code>npm init</code>. Follow the on-screen prompts to create the project. Then, create the rest of the folders (except for <code>node_modules</code><em>)</em> as described above. Your project structure should look like this:</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*jLNggGBoQ6A6xnqVyltFEA.png" alt="Image" width="660" height="438" loading="lazy">

<p><em>The simplest Vue SFC project structure.</em></p>
<h4 id="heading-step-2-install-the-dependencies">Step 2: Install the dependencies</h4>
<p>Here is a quick rundown of the dependencies we are using:</p>
<p><strong>vue</strong>: The JavaScript framework</p>
<p><strong>vue-loader and vue-template-compiler</strong>: Used to convert our Vue files into JavaScript.</p>
<p><strong>webpack</strong>: The tool that will allow us to pass our code through some transformations and bundle it into one file.</p>
<p><strong>webpack-cli:</strong> Needed to run the Webpack commands.</p>
<p><strong>webpack-dev-server</strong>: Although not needed for our small project (since we won’t be making any HTTP requests), we will still “serve” our project from a development server.</p>
<p><strong>babel-loader</strong>: Transform our ES6 code into ES5. (It needs help from the next two dependencies.)</p>
<p><strong>@babel/core and @babel/preset-env</strong>: Babel by itself doesn’t do anything to your code. These two “add-ons” will allow us to transform our ES6 code into ES5 code.</p>
<p><strong>css-loader:</strong> Takes the CSS we write in our <code>.vue</code> files or any CSS we might import into any of our JavaScript files and resolve the path to those files. In other words, figure out where the CSS is. This is another loader that by itself won’t do much. We need the next loader to actually do something with the CSS.</p>
<p><strong>vue-style-loader</strong>: Take the CSS we got from our <code>css-loader</code> and inject it into our HTML file. This will create and inject a style tag in the head of our HTML document.</p>
<p><strong>html-webpack-plugin</strong>: Take our <em>index.html</em> and inject our bundled JavaScript file in the head. Then, copy this file into the <code>dist</code> folder.</p>
<p><strong>rimraf</strong>: Allows us, from the command line, to delete files. This will come in handy when we build our project multiple times. We will use this to delete any old builds.</p>
<p>Let’s install these dependencies now. From the command line, run:</p>
<pre><code class="language-bash">npm install vue vue-loader vue-template-compiler webpack webpack-cli webpack-dev-server babel-loader @babel/core @babel/preset-env css-loader vue-style-loader html-webpack-plugin rimraf -D
</code></pre>
<p><em><strong>Note:</strong></em> <em>The “-D” at the end marks each dependency as a development dependency in our package.json. We are bundling all dependencies in one file, so, for our small project, we have no production dependencies.</em></p>
<h4 id="heading-step-3-create-the-files-except-for-our-webpack-configuration-file">Step 3: Create the files (Except for our Webpack configuration file).</h4>
<pre><code class="language-js">&lt;template&gt;
  &lt;div id="app"&gt;
    {{ message }}
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  data() {
    return {
      message: 'Hello World',
    };
  },
};
&lt;/script&gt;

&lt;style&gt;
#app {
  font-size: 18px;
  font-family: 'Roboto', sans-serif;
  color: blue;
}
&lt;/style&gt;
</code></pre>
<pre><code class="language-html">&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;Vue Hello World&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id="app"&gt;&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<pre><code class="language-js">import Vue from 'vue';
import App from './App.vue';

new Vue({
  el: '#app',
  render: h =&gt; h(App),
});
</code></pre>
<pre><code class="language-js">module.exports = {
  presets: ['@babel/preset-env'],
}
</code></pre>
<p>Up to this point, nothing should look too foreign. I’ve kept each file very basic. I’ve only added minimal CSS and JS to see our workflow in action.</p>
<h4 id="heading-step-4-instructing-webpack-what-to-do">Step 4: Instructing Webpack what to do</h4>
<p>All the configuration Webpack needs access to is now present. We need to do two final things: Tell Webpack what to do and run Webpack.</p>
<p>Below is the Webpack configuration file (<code>webpack.config.js</code>). Create this file in the projects root directory. Line-by-line we’ll discuss what is occurring.</p>
<pre><code class="language-js">const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
  entry: './src/main.js',
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader' },
      { test: /\.vue$/, use: 'vue-loader' },
      { test: /\.css$/, use: ['vue-style-loader', 'css-loader']},
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
    new VueLoaderPlugin(),
  ]
};
</code></pre>
<p><strong>Lines 1 and 2:</strong> We are importing the two plugins we use below. Notice, our loaders don’t normally need to be imported, just our plugins. And in our case, the <code>vue-loader</code> (which we use in line 9) also needs a plugin to work (however, Babel, for example, does not).</p>
<p><strong>Line 4:</strong> We export our configuration as an object. This gives us access to it when we run the Webpack commands.</p>
<p><strong>Line 5:</strong> This is our entry module. Webpack needs a place to start. It looks in our <code>main.js</code> file and then starts to comb through our code from that point.</p>
<p><strong>Line 6 and 7:</strong> This is the module object. Here, we primarily pass in an array of rules. Each rule tells Webpack how to handle certain files. So, while Webpack uses the entry point of <code>main.js</code> to start combing through our code, it uses the rules to transform our code.</p>
<p><strong>Line 8 (rule):</strong> This rule instructs Webpack to use the <code>babel-loader</code> on any files which end with <code>.js</code><em>.</em> Remember, Babel will transform ES6+ to ES5.</p>
<p><strong>Line 9 (rule):</strong> This rule instructs Webpack to use <code>vue-loader</code> (and don’t forget the associated plugin on line 17) to transform our <code>.vue</code> files into JavaScript.</p>
<p><strong>Line 10 (rule):</strong> Sometimes we want to pass a file through two loaders. Counterintuitively, Webpack will pass the file from right to left instead of left to right. Here we are using two loaders and saying to Webpack: “get my CSS from my Vue file or any JavaScript files(<code>css-loader</code>) and inject it into my HTML as a style tag (<code>vue-style-loader</code>).</p>
<p><strong>Lines 11 and 12:</strong> Close out our rules array and module object.</p>
<p><strong>Lines 13:</strong> Create a plugins array. Here we will add the two plugins we need.</p>
<p><strong>Line: 14 -16 (plugin):</strong> The <code>HtmlWebpackPlugin</code> takes the location of our <em>index.html</em> file and adds our bundled JavaScript file to it via a script tag. This plugin will also copy the HTML file to our distribution folder when we build our project.</p>
<p><strong>Line 17 (plugin):</strong> The <code>VueLoaderPlugin</code> works with our <code>vue-loader</code> to parse our <code>.vue</code> files.</p>
<p><strong>Line 18:</strong> Close out the plugins array.</p>
<p><strong>Line 19:</strong> Close out the Webpack object that we are exporting.</p>
<h4 id="heading-step-5-setting-up-our-packagejson-file-so-we-can-run-webpack">Step 5: Setting up our package.json file so we can run Webpack</h4>
<p>Our configuration is complete, now we want to see our application. Ideally, as we make changes to our application, the browser would update automatically. This is possible with <code>webpack-dev-server</code>.</p>
<p>Delete the <code>test</code> script in our <code>package.json</code> file, and replace it with a script to serve our application:</p>
<pre><code class="language-json">
{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "serve": "webpack-dev-server --mode development"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.1.6",
    "@babel/preset-env": "^7.1.6",
    "babel-loader": "^8.0.4",
    "css-loader": "^1.0.1",
    "html-webpack-plugin": "^3.2.0",
    "rimraf": "^2.6.2",
    "vue": "^2.5.17",
    "vue-loader": "^15.4.2",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.5.17",
    "webpack": "^4.26.0",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  },
  "dependencies": {}
}
</code></pre>
<p>The name of this command is your choice. I chose to call mine <code>serve</code> since we will be <em>serving</em> our application.</p>
<p>From our terminal or command line, we can run <code>npm run serve</code> and that in turn will run <code>webpack-dev-server --mode development</code> .</p>
<p><em>The</em> <code>--mode development</code> is what’s called a flag or option. We haven’t talked about this, but it essentially instructs Webpack that you are in development mode. We can also pass in <code>--mode production</code> which we will do when we build our project. These aren’t necessarily required for Webpack to work. Without these, you will get a warning message telling you to provide a mode when you run Webpack .</p>
<p><em>I say “necessarily required” because Webpack will minimize our code in production mode but not in development. So, don’t think those commands don’t do anything–they do.</em></p>
<p>Let’s run <code>npm run serve</code> and see what happens.</p>
<p>When we run <code>npm run serve</code> we get some output in our terminal. And, if everything goes well:</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*UNoqxigEpgvVZRjs2VqxTA.png" alt="Image" width="493" height="85" loading="lazy">

<p>And if we scroll up a bit:</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*ye4_gCeGPXcwgPcGUQf_rg.png" alt="Image" width="800" height="112" loading="lazy">

<p>Point your browser to <code>http://localhost:8080</code>. You will see your Blue Hello World message in Roboto font.</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*kKYxmKJ_rTBzT7rOvjZtgg.png" alt="Image" width="800" height="129" loading="lazy">

<p>Now, let’s update the project and change the message to <code>Hello Universe</code>. Notice that the webpage refreshes automatically. That’s great, right? Can you think of a downside?</p>
<p>Let’s change the application just a bit and include an input which we will bind a variable to (with <code>v-model</code>). We will output the variable in an <code>&lt;h2&gt;</code>tag below the input. I’ve also updated the styling section to style the message now. Our <code>App.vue</code> file should look like this:</p>
<pre><code class="language-js">&lt;template&gt;
  &lt;div id="app"&gt;
    &lt;input
      v-model="message"
      type="text"&gt;
      &lt;h2 class="message"&gt;{{ message }}&lt;/h2&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  data() {
    return {
      message: 'Hello world!',
    };
  },
};
&lt;/script&gt;

&lt;style&gt;
.message {
  font-size: 18px;
  font-family: 'Roboto', sans-serif;
  color: blue;
}
&lt;/style&gt;
</code></pre>
<p>When we serve our application, we will have an input with a message of <code>Hello World</code> below it. The input is bound to the <code>message</code> variable, so as we type, we change the <code>&lt;h2&gt;</code> content. Go ahead, type into the input to change the <code>&lt;h2&gt;</code>content.</p>
<p>Now go back to your editor, and below the <code>&lt;h2&gt;</code>tag, add the following:</p>
<p><code>&lt;h3&gt;Some Other Message&lt;/h3&gt;</code></p>
<p>Save your <code>App.vue</code> and watch what happens.</p>
<p>The <code>h2</code> we just updated by typing in our input reverted back to <code>Hello World</code>. This is because the browser actually refreshes, and the script tag and page are loaded again. In other words, we were not able to maintain the state of our application. This may not seem like a big deal, but as you are testing your application and adding data to it, it will be frustrating if your app “resets” every time. Fortunately, Webpack offers us a solution called Hot Module Replacement.</p>
<p>The hot module replacement is a plugin provided by Webpack itself. Up until this point, we have not used the Webpack object itself in our configuration file. However, we will now import Webpack so we can access the plugin.</p>
<p>In addition to the plugin, we will pass one additional option to Webpack, the <code>devServer</code> option. In that option, we will set <code>hot</code> to <code>true</code>. Also, we will make an (optional) update to our build workflow: We will open the browser window automatically when we run <code>npm run serve</code>. We do this by setting <code>open</code> to <code>true</code> which is also inside the <code>devServer</code> option.</p>
<pre><code class="language-js">const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const webpack = require('webpack');

module.exports = {
  entry: './src/main.js',
  module: {
    rules: [
      { test: /\.js$/, use: 'babel-loader' },
      { test: /\.vue$/, use: 'vue-loader' },
      { test: /\.css$/, use: ['vue-style-loader', 'css-loader']},
    ]
  },
  devServer: {
    open: true,
    hot: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
    new VueLoaderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
  ]
};
</code></pre>
<p>Notice that we’ve imported Webpack so we could access the <code>hotModuleReplacementPlugin</code>. We’ve added that to the <code>plugins</code> array, and then told Webpack to use it with <code>hot: true</code>. We open the browser window automatically when we serve the application with <code>open: true</code>.</p>
<p>Run <code>npm run serve</code>:</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*59LVTDT3pEk3RzQ7dodmvw.png" alt="Image" width="800" height="161" loading="lazy">

<p>The browser window should open, and if you open your dev tools, you should notice a slight change in the output. It now tells us hot module replacement is enabled. Let’s type in our input to change the <code>&lt;h2&gt;</code> content. Then, change the<code>h3</code> tag to read: <code>One More Message</code>.</p>
<p>Save your file and notice what happens.</p>
<p>The browser doesn't refresh, but our <code>&lt;h3&gt;</code>change is reflected! The message we typed in the input remains, but the <code>h3</code> updates. This allows our application to keep it’s state while we edit it.</p>
<h4 id="heading-step-7-building-our-project">Step 7: Building our project</h4>
<p>So far, we’ve served our application. But, what if we want to build our application so we can distribute it?</p>
<p>If you noticed, when we serve our application, no files are created. Webpack creates a version of these files that only exist in temporary memory. If we want to distribute our Hello World app to our client, we need to <em>build</em> the project.</p>
<p>This is very simple. Just like before, we will create a script in our package.json file to tell Webpack to build our project. We will use <code>webpack</code> as the command instead of <code>webpack-dev-server</code>. We will pass in the <code>--mode production</code> flag as well.</p>
<p>We will also use the <code>rimraf</code> package first to delete any previous builds we may have. We do this simply by <code>rimraf dist</code>.</p>
<p><code>_dist_</code> <em>is the folder Webpack will automatically create when it builds our project. “Dist” is short for distribution–i.e. we are “distributing” our applications code.</em></p>
<p>The <code>rimraf dist</code> command is telling the <code>rimraf</code> package to delete the <code>dist</code> directory. <strong>Make sure you don’t</strong> <code>rimraf src</code> <strong>by accident!</strong></p>
<p><em>Webpack also offers a plugin that will accomplish this cleaning process called</em> <code>clean-webpack-plugin</code>. I chose <code>dist</code> show an alternative way.</p>
<p>Our package.json file should look like this:</p>
<pre><code class="language-json">{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "clean": "rimraf dist",
    "build": "npm run clean &amp;&amp; webpack --mode production",
    "serve": "webpack-dev-server --mode development"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.1.6",
    "@babel/preset-env": "^7.1.6",
    "babel-loader": "^8.0.4",
    "css-loader": "^1.0.1",
    "html-webpack-plugin": "^3.2.0",
    "rimraf": "^2.6.2",
    "vue": "^2.5.17",
    "vue-loader": "^15.4.2",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.5.17",
    "webpack": "^4.26.0",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  },
  "dependencies": {}
}
</code></pre>
<p>There are three things to notice:</p>
<ol>
<li><p>I’ve created a separate <code>clean</code> script so we can run it independently of our build script.</p>
</li>
<li><p><code>npm run build</code> will call the independent <code>clean</code> script we’ve created.</p>
</li>
<li><p>I have <code>&amp;&amp;</code> between <code>npm run clean</code> and <code>webpack</code>. This instruction says: “run <code>npm run clean</code> first, <em>then</em> run <code>webpack</code>”.</p>
</li>
</ol>
<p>Let’s build the project.</p>
<p><code>npm run build</code></p>
<p>Webpack creates a <code>dist</code> directory, and our code is inside. Since our code makes no HTTP requests, we can simply open our <code>index.html</code> file in our browser and it will work as expected.</p>
<p><em>If we had code that was making HTTP requests, we would run into some cross-origin errors as we made those requests. We would need to run that project from a server for it to work.</em></p>
<p>Let’s examine the <code>index.html</code> that Webpack created in the browser and the code editor.</p>
<img src="https://cdn-media-1.freecodecamp.org/images/1*Y0hwAs2CRmCuBrn7h1pFUw.png" alt="Image" width="800" height="128" loading="lazy">

<p><em>R</em></p>
<p>If we open it in our editor or take a look at the source code in our dev tools you will see Webpack injected the script tag. In our editor though, you won’t see the styles because the style tag is injected dynamically at runtime with JavaScript!</p>
<p>Also, notice our development console information is no longer present. This is because we passed the <code>--production</code> flag to Webpack.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Understanding the build process behind the frameworks you use will help you to better understand the framework itself. Take some time to try to build an Angular, React or another Vue Project without the use of the respective CLIs. Or, just build a basic three-file site (index.html, styles.css, and app.js), but use Webpack to serve and build a production version.</p>
<p>Thanks for reading!</p>
<p>Found this helpful? I write about practical automation, productivity systems, and building smarter workflows — without the jargon. Visit me at <a href="http://brandonwoz.com">brandonwoz.com</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Cross-Origin Resource Sharing requests affect your app’s performance ]]>
                </title>
                <description>
                    <![CDATA[ By Ankur Anand The title may lead you to think that this post is another ranting post about the downside of a “Single Page Application”. It is more about shedding some light on the performance perspective to keep in mind while designing the SPA. Espe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-terrible-performance-cost-of-cors-api-on-the-single-page-application-spa-6fcf71e50147/</link>
                <guid isPermaLink="false">66d45d98052ad259f07e4a57</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sun, 02 Sep 2018 09:43:09 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*SFc7VmGOTTIKCRTiQSc96Q.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ankur Anand</p>
<p>The title may lead you to think that this post is another ranting post about the downside of a “Single Page Application”. It is more about shedding some light on the <em>performance perspective to keep in mind while designing the SPA.</em> Especially if your SPA consumes APIs from different domain services.</p>
<p>If you are designing an SPA which consumes the API from the same domain of the SPA, then great. You should skip this article if your SPA only pulls from the API on the same domain.</p>
<p>Most SPAs involve “microservices.” They consume different endpoints of services serving by different domains within the SPA. This adds resilience, fault tolerance, and an improved user experience of our product. Multiple domain requests become inevitable until and unless we strictly adhere to the same domain app <strong>API Gateway — Microservices Pattern</strong> for our SPA.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/PcHWxtFEw5vzZI3anByzWY67x-uPCaIAOFd4" alt="Image" width="465" height="215" loading="lazy">
<em>SPA + Cors does not always reduce the latency.</em></p>
<p>Let’s Imagine we have a <code>GET</code> API <code>/users/report/:id</code> served from domain <code>api.example.com</code>. Our SPA is served from <code>spa.example.com</code>. The <code>:id</code> means its a value that can change for every request.</p>
<p>Can you guess the issue with the above API design with respect to <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a> (Cross-Origin Resource Sharing) and its impact on the performance of our SPA?</p>
<p>Here’s a brief Introduction of CORS from <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">MDN</a>:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xvd3slhsPc14xXUGV4GnBNqmK8PzfJIwNSHO" alt="Image" width="800" height="478" loading="lazy"></p>
<p>CORS is all good while it’s a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests">simple request</a> that doesn’t trigger a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests">CORS</a> preflight. But most often we make requests that are not a “ <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests">simple request</a>.”</p>
<p>This is due to the fact that we need to send a header that is not <a target="_blank" href="https://fetch.spec.whatwg.org/#cors-safelisted-request-header">CORS-safelisted-request-header</a>. An example header is <code>Authorization, x-corelation-id</code>. Frequently our <code>Content-Type</code> header value is <code>application/json</code>. This is not an allowed value for the <code>[Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)</code> header for <a target="_blank" href="https://fetch.spec.whatwg.org/#cors-safelisted-request-header">cors-safelisted-request-header</a>.</p>
<p>If our <code>api.example.com</code> server accepts <code>content-type</code> of <code>application/json</code>, our SPA domain <code>spa.example.com</code> will first send an HTTP request by the <code>[OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS)</code> method. It is sent to the resource <code>/users/report/12345</code> on the other domain <code>api.example.com</code>. To determine whether the actual request is safe to send, the option is sent preflighted. Cross-site requests are always preflighted like this, since they may have implications for user data.</p>
<p>It’s the job of <code>api.example.com</code> server to let the other domain <code>spa.example.com</code> know it’s safe to send the data. You might have done something similar to this for CORS inside your Application.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/EPbbVhWxAMi9cCXNxD2x-L3qGSu5dACKlOIB" alt="Image" width="800" height="341" loading="lazy">
<em>Allowing CORS on Express.js Server</em></p>
<p>Once the <code>api.example.com</code> server sends the proper response from “OPTIONS” method to other domain <code>spa.example.com</code> then only the actual data with the request you were trying to make is done.</p>
<blockquote>
<p><em>So to access a resource <code>api.example.com/users/report/12345</code> <strong>two actual requests were performed.</strong></em></p>
</blockquote>
<p>You might say yes. We can use the <code>[Access-Control-Max-Age header](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests#Access-Control-Max-Age)</code> to cache the results of a preflight request. The next time we access the resource <code>api.example.com/users/report/12345</code> from <code>spa.example.com</code> there is no preflight request.</p>
<p>Yes, that’s true, but then remember the title — The terrible performance cost of <strong>CORS</strong> requests on the single-page application (SPA). This comes from the API that we’re are consuming and the way it’s been designed. In our example, we designed our API <code>/users/report/:id</code>, where <code>:id</code> means its a value that can change.</p>
<blockquote>
<p><em>The way preflight cache works is per URL, not just the origin. This means that any change in the path (which includes query parameters) warrants another preflight request.</em></p>
</blockquote>
<p>So in our case, to access resource <code>api.example.com/users/report/12345</code> and <code>api.example.com/users/report/123987</code>, it will trigger four requests from our SPA in total.</p>
<p>If you have a slow network, this could be a huge setback. Especially when an OPTIONS request takes 2 seconds to respond, and another 2 for the data.</p>
<p><strong>Now imagine your SPA application making millions of such requests for different domains.</strong> It will have a terrible impact on your SPA’s performance. You’re doubling the latency of every request.</p>
<blockquote>
<p><em>SPAs are great in their own domain. But for consuming different domains they come with their own cost. If the API is poorly designed, the latency issues of your SPA can hurt more than the benefits they provide.</em></p>
</blockquote>
<p>There is no solution or technology that is wholly good or bad. Knowing its shortcoming and what it takes to make it work are what matters. This is what differentiates your Application from the others.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a single page application using Razor pages with Blazor ]]>
                </title>
                <description>
                    <![CDATA[ By Ankit Sharma In this article, we are going to create a Single Page Application (SPA) using Razor pages in Blazor, with the help of the Entity Framework Core database first approach. Introduction Single Page Application (SPAs) are web applications ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-single-page-application-using-razor-pages-with-blazor-9d010fd6be45/</link>
                <guid isPermaLink="false">66d45da4787a2a3b05af4382</guid>
                
                    <category>
                        <![CDATA[ entity framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 11 Jun 2018 16:21:44 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*vVXicFhmOOyYASuzkM570w.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ankit Sharma</p>
<p>In this article, we are going to create a Single Page Application (SPA) using Razor pages in Blazor, with the help of the Entity Framework Core database first approach.</p>
<h3 id="heading-introduction">Introduction</h3>
<p>Single Page Application (SPAs) are web applications that load a single HTML page, and dynamically update that page as the user interacts with the app. We will be creating a sample Employee Record Management System and perform CRUD operations on it.</p>
<p>We will be using Visual Studio 2017 and SQL Server 2014.</p>
<p>Take a look at the final application.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/pA8DDQa-ek6BJEKFbMN8ukjS2nv8fFxMJ9In" alt="Image" width="650" height="387" loading="lazy"></p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li>install .NET Core 2.1 Preview 2 SDK from <a target="_blank" href="https://www.microsoft.com/net/download/dotnet-core/sdk-2.1.300-preview2">here</a></li>
<li>install Visual Studio 2017 v15.7 or above from <a target="_blank" href="https://www.visualstudio.com/downloads/">here</a></li>
<li>install ASP.NET Core Blazor Language Services extension from <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=aspnet.blazor">here</a></li>
<li>SQL Server 2008 or above</li>
</ul>
<p>The Blazor framework is not supported by versions below Visual Studio 2017 v15.7.</p>
<h3 id="heading-source-code">Source code</h3>
<p>Get the source code from <a target="_blank" href="https://github.com/AnkitSharma-007/SPA-With-Blazor">GitHub</a>.</p>
<h3 id="heading-creating-the-table">Creating the table</h3>
<p>We will be using a DB table to store all the records of employees.</p>
<p>Open SQL Server and use the following script to create the<code>Employee</code> table.</p>
<pre><code>CREATE TABLE Employee (  EmployeeID int IDENTITY(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>) PRIMARY KEY,  Name varchar(<span class="hljs-number">20</span>) NOT NULL ,  City varchar(<span class="hljs-number">20</span>) NOT NULL ,  Department varchar(<span class="hljs-number">20</span>) NOT NULL ,  Gender varchar(<span class="hljs-number">6</span>) NOT NULL   )
</code></pre><h3 id="heading-create-the-blazor-web-application">Create the Blazor web application</h3>
<p>Open Visual Studio and select “File” &gt; “New” &gt; “Project”.</p>
<p>After selecting the project, a “New Project” dialog will open. In the left panel, select “.NET Core” inside the Visual C# menu. Then, select “ASP.NET Core Web Application” from available project types. Put the name of the project as “BlazorSPA” and press “OK”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Mzs4TvnFTePBJQ5wDxkyXwI7VFBjDHUAVZ4e" alt="Image" width="650" height="371" loading="lazy"></p>
<p>After clicking on “OK”, a new dialog will open asking you to select the project template. You can observe two drop-down menus at the top left of the template window. Select “.NET Core” and “ASP.NET Core 2.0” from these dropdowns. Then, select the “Blazor (ASP.NET Core hosted)” template and press “OK”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/GyYuYmaGwKBRknv04cVsMhZzS65vVbH7qRm3" alt="Image" width="650" height="457" loading="lazy"></p>
<p>Now our Blazor solution will be created. You can observe the folder structure in Solution Explorer as shown in the below image.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/n69NyMxPfNGl7qLqWWrx8YmQOiQeXJy6qUyN" alt="Image" width="268" height="544" loading="lazy"></p>
<p>You can observe that we have three project files created inside this solution.</p>
<ol>
<li>BlazorSPA.Client — has the client side code and contains the pages that will be rendered on the browser.</li>
<li>BlazorSPA.Server — has the server side codes such as DB-related operations and the web API.</li>
<li>BlazorSPA.Shared — contains the shared code that can be accessed by both client and server. It contains our model classes.</li>
</ol>
<h3 id="heading-scaffolding-the-model-to-the-application">Scaffolding the model to the application</h3>
<p>We are using the Entity Framework core database first approach to create our models. We will create our model class in the “BlazorSPA.Shared” project so that it can be accessible to both client and server project.</p>
<p>Navigate to “Tools” &gt; “NuGet Package Manager” &gt; “Package Manager Console”. Select “BlazorSPA.Shared” from the “Default project” dropdown. Refer to the image below:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/SjjP3ntvRWEycZ-3dX20CSBzezpMxs645EGF" alt="Image" width="650" height="71" loading="lazy"></p>
<p>First, we will install the package for the database provider that we are targeting, which is SQL Server in this case. Run the following command:</p>
<pre><code>Install-Package Microsoft.EntityFrameworkCore.SqlServer
</code></pre><p>Since we are using Entity Framework Tools to create a model from the existing database, we will install the tools package as well. Run the following command:</p>
<pre><code>Install-Package Microsoft.EntityFrameworkCore.Tools
</code></pre><p>After you have installed both the packages, we will scaffold our model from the database tables using the following command:</p>
<pre><code>Scaffold-DbContext <span class="hljs-string">"Your connection string here"</span> Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables Employee
</code></pre><p><strong>Do not forget</strong> to put your own connection string (inside <code>“”</code>). After this command is executed successfully, you can observe a “Models” folder has been created. It contains two class files, “myTestDBContext.cs” and “Employee.cs”. Hence, we have successfully scaffolded our models using the Entity Framework core database first approach.</p>
<p>At this point in time, the Models folder will have the following structure:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/gY0lV5KolvXUTFdFkN-gnXvzb0aObcOmAXZz" alt="Image" width="308" height="281" loading="lazy"></p>
<h3 id="heading-creating-the-data-access-layer-for-the-application">Creating the data access layer for the application</h3>
<p>Right-click on the “BlazorSPA.Server” project and then select “Add” &gt; “New Folder” and name the folder as “DataAccess”. We will be adding our class to handle database-related operations inside this folder only.</p>
<p>Right click on the “DataAccess” folder and select “Add” &gt; “Class”. Name your class “EmployeeDataAccessLayer.cs”.</p>
<p>Open “EmployeeDataAccessLayer.cs” and put the following code into it:</p>
<pre><code>using BlazorSPA.Shared.Models;using Microsoft.EntityFrameworkCore;using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace BlazorSPA.Server.DataAccess{    public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmployeeDataAccessLayer</span>    </span>{        myTestDBContext db = <span class="hljs-keyword">new</span> myTestDBContext();        <span class="hljs-comment">//To Get all employees details           public IEnumerable&lt;Employee&gt; GetAllEmployees()        {            try            {                return db.Employee.ToList();            }            catch            {                throw;            }        }        //To Add new employee record             public void AddEmployee(Employee employee)        {            try            {                db.Employee.Add(employee);                db.SaveChanges();            }            catch            {                throw;            }        }        //To Update the records of a particluar employee            public void UpdateEmployee(Employee employee)        {            try            {                db.Entry(employee).State = EntityState.Modified;                db.SaveChanges();            }            catch            {                throw;            }        }        //Get the details of a particular employee            public Employee GetEmployeeData(int id)        {            try            {                Employee employee = db.Employee.Find(id);                return employee;            }            catch            {                throw;            }        }        //To Delete the record of a particular employee            public void DeleteEmployee(int id)        {            try            {                Employee emp = db.Employee.Find(id);                db.Employee.Remove(emp);                db.SaveChanges();            }            catch            {                throw;            }        }    }}</span>
</code></pre><p>Here we have defined methods to handle database operations. <code>GetAllEmployees</code> will fetch all the employee data from the Employee Table. Similarly, <code>AddEmployee</code> will create a new employee record, and <code>UpdateEmployee</code> will update the record of an existing employee. <code>GetEmployeeData</code> will fetch the record of the employee corresponding to the employee ID passed to it, and <code>DeleteEmployee</code> will delete the employee record corresponding to the employee ID passed to it.</p>
<h3 id="heading-adding-the-web-api-controller-to-the-application">Adding the web API controller to the application</h3>
<p>Right click on the “BlazorSPA.Server/Controllers” folder and select “Add” &gt; “New Item”. An “Add New Item” dialog box will open. Select “ASP.NET” from the left panel, then select “API Controller Class” from the templates panel, and put the name as “EmployeeController.cs”. Click “Add”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/OZjyShEwULZNifr-ZNzRWY0bjYkAaQKHMNeI" alt="Image" width="650" height="370" loading="lazy"></p>
<p>This will create our API <code>EmployeeController</code> class.</p>
<p>We will call the methods of the<code>EmployeeDataAccessLayer</code> class to fetch data and pass on the data to the client side.</p>
<p>Open “EmployeeController.cs” file and put the following code into it:</p>
<pre><code>using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using BlazorSPA.Server.DataAccess;using BlazorSPA.Shared.Models;using Microsoft.AspNetCore.Mvc;namespace BlazorSPA.Server.Controllers{    public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmployeeController</span> : <span class="hljs-title">Controller</span>    </span>{        EmployeeDataAccessLayer objemployee = <span class="hljs-keyword">new</span> EmployeeDataAccessLayer();        [HttpGet]        [Route(<span class="hljs-string">"api/Employee/Index"</span>)]        public IEnumerable&lt;Employee&gt; Index()        {            <span class="hljs-keyword">return</span> objemployee.GetAllEmployees();        }        [HttpPost]        [Route(<span class="hljs-string">"api/Employee/Create"</span>)]        public <span class="hljs-keyword">void</span> Create([FromBody] Employee employee)        {            <span class="hljs-keyword">if</span> (ModelState.IsValid)                objemployee.AddEmployee(employee);        }        [HttpGet]        [Route(<span class="hljs-string">"api/Employee/Details/{id}"</span>)]        public Employee Details(int id)        {            <span class="hljs-keyword">return</span> objemployee.GetEmployeeData(id);        }        [HttpPut]        [Route(<span class="hljs-string">"api/Employee/Edit"</span>)]        public <span class="hljs-keyword">void</span> Edit([FromBody]Employee employee)        {            <span class="hljs-keyword">if</span> (ModelState.IsValid)                objemployee.UpdateEmployee(employee);        }        [HttpDelete]        [Route(<span class="hljs-string">"api/Employee/Delete/{id}"</span>)]        public <span class="hljs-keyword">void</span> Delete(int id)        {            objemployee.DeleteEmployee(id);        }    }}
</code></pre><p>At this point of time, our “BlazorSPA.Server” project has the following structure.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/DnpONEsQobR0N3YDjCyWRteruR0P-8CvxFug" alt="Image" width="369" height="423" loading="lazy"></p>
<p>We are done with our backend logic. Therefore, we will now proceed to code our client side.</p>
<h3 id="heading-adding-the-razor-page-to-the-application">Adding the Razor page to the application</h3>
<p>We will add the Razor page into the “BlazorSPA.Client/Pages” folder. By default, we have “Counter” and “Fetch Data” pages provided in our application. These default pages will not affect our application but, for the sake of this tutorial, we will delete the “fetchdata” and “counter” pages from the “BlazorSPA.Client/Pages” folder.</p>
<p>Right click on the “BlazorSPA.Client/Pages” folder and then select “Add” &gt; “New Item”. An “Add New Item” dialog box will open. Select “ASP.NET Core” from the left panel, then select “Razor Page” from the templates panel and name it “EmployeeData.cshtml”. Click “Add”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/tR2ue0vJJqAb0HROVGJxob8ZJy13WgbFUav7" alt="Image" width="650" height="370" loading="lazy"></p>
<p>This will add an “EmployeeData.cshtml” page to our “BlazorSPA.Client/Pages” folder. This Razor page will have two files, “EmployeeData.cshtml” and _“<em>EmployeeData.cshtml.cs”</em>._</p>
<p>Now we will add code to these pages.</p>
<h3 id="heading-employeedatacshtmlcs">EmployeeData.cshtml.cs</h3>
<p>Open “EmployeeData.cshtml.cs” and put the following code into it:</p>
<pre><code>using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Threading.Tasks;using BlazorSPA.Shared.Models;using Microsoft.AspNetCore.Blazor;using Microsoft.AspNetCore.Blazor.Components;using Microsoft.AspNetCore.Blazor.Services;namespace BlazorSPA.Client.Pages{    public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmployeeDataModel</span> : <span class="hljs-title">BlazorComponent</span>    </span>{        [Inject]        protected HttpClient Http { get; set; }        [Inject]        protected IUriHelper UriHelper { get; set; }        [Parameter]        protected string paramEmpID { get; set; } = <span class="hljs-string">"0"</span>;        [Parameter]        protected string action { get; set; }        protected List&lt;Employee&gt; empList = <span class="hljs-keyword">new</span> List&lt;Employee&gt;();        protected Employee emp = <span class="hljs-keyword">new</span> Employee();        protected string title { get; set; }        protected override <span class="hljs-keyword">async</span> Task OnParametersSetAsync()        {            <span class="hljs-keyword">if</span> (action == <span class="hljs-string">"fetch"</span>)            {                <span class="hljs-keyword">await</span> FetchEmployee();                <span class="hljs-built_in">this</span>.StateHasChanged();            }            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (action == <span class="hljs-string">"create"</span>)            {                title = <span class="hljs-string">"Add Employee"</span>;                emp = <span class="hljs-keyword">new</span> Employee();            }            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (paramEmpID != <span class="hljs-string">"0"</span>)            {                <span class="hljs-keyword">if</span> (action == <span class="hljs-string">"edit"</span>)                {                    title = <span class="hljs-string">"Edit Employee"</span>;                }                <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (action == <span class="hljs-string">"delete"</span>)                {                    title = <span class="hljs-string">"Delete Employee"</span>;                }                emp = <span class="hljs-keyword">await</span> Http.GetJsonAsync&lt;Employee&gt;(<span class="hljs-string">"/api/Employee/Details/"</span> + Convert.ToInt32(paramEmpID));            }        }        protected <span class="hljs-keyword">async</span> Task FetchEmployee()        {            title = <span class="hljs-string">"Employee Data"</span>;            empList = <span class="hljs-keyword">await</span> Http.GetJsonAsync&lt;List&lt;Employee&gt;&gt;(<span class="hljs-string">"api/Employee/Index"</span>);        }        protected <span class="hljs-keyword">async</span> Task CreateEmployee()        {            <span class="hljs-keyword">if</span> (emp.EmployeeId != <span class="hljs-number">0</span>)            {                <span class="hljs-keyword">await</span> Http.SendJsonAsync(HttpMethod.Put, <span class="hljs-string">"api/Employee/Edit"</span>, emp);            }            <span class="hljs-keyword">else</span>            {                <span class="hljs-keyword">await</span> Http.SendJsonAsync(HttpMethod.Post, <span class="hljs-string">"/api/Employee/Create"</span>, emp);            }            UriHelper.NavigateTo(<span class="hljs-string">"/employee/fetch"</span>);        }        protected <span class="hljs-keyword">async</span> Task DeleteEmployee()        {            <span class="hljs-keyword">await</span> Http.DeleteAsync(<span class="hljs-string">"api/Employee/Delete/"</span> + Convert.ToInt32(paramEmpID));            UriHelper.NavigateTo(<span class="hljs-string">"/employee/fetch"</span>);        }        protected <span class="hljs-keyword">void</span> Cancel()        {            title = <span class="hljs-string">"Employee Data"</span>;            UriHelper.NavigateTo(<span class="hljs-string">"/employee/fetch"</span>);        }    }}
</code></pre><p>Let us understand this code. We have defined a class <code>EmployeeDataModel</code> that will hold all our methods that we will use in the “EmployeeData.cshtml” page.</p>
<p>We are injecting the <code>HttpClient</code> service to enable web API call and the <code>IUriHelper</code> service to enable URL redirection. After this, we have defined our parameter attributes — <code>paramEmpID</code> and <code>action</code>. These parameters are used in “EmployeeData.cshtml” to define the routes for our page. We have also declared a property <code>title</code> to display the heading to specify the current action that is being performed on the page.</p>
<p>The <code>OnParametersSetAsync</code> method is invoked every time the URL parameters are set for the page. We will check the value of parameter <code>action</code> to identify the current operation on the page.</p>
<p>If the action is set to <code>fetch</code>, then we will invoke the <code>FetchEmployee</code> method to fetch the updated list of employees from the database and refresh the UI using the <code>StateHasChanged</code> method.</p>
<p>We will check if the action attribute of parameter is set to <code>create</code>, then we will set the title of the page to “Add Employee” and create a new object of type <code>Employee</code>. If the <code>paramEmpID</code> is not “0”, then it is either an <code>edit</code> action or a <code>delete</code> action. We will set the title property accordingly and then invoke our web API method to fetch the data for the employee ID as set in the <code>paramEmpID</code> property.</p>
<p>The method <code>FetchEmployee</code> will set the title to “Employee Data” and fetch all the employee data by invoking our web API method.</p>
<p>The <code>CreateEmployee</code> method will check if it is invoked to add a new employee record, or to edit an existing employee record. If the <code>EmployeeId</code> property is set, then it is an <code>edit</code> request and we will send a PUT request to the web API. If <code>EmployeeId</code> is not set, then it is a <code>create</code> request and we will send a POST request to web API. We will set the <code>title</code> property according to the corresponding value of action, and then invoke our web API method to fetch the data for the employee ID as set in the <code>paramEmpID</code> property.</p>
<p>The <code>DeleteEmployee</code> method will delete the employee record for the employee ID as set in the <code>paramEmpID</code> property. After deletion, the user is redirected to the “/employee/fetch” page.</p>
<p>In the <code>Cancel</code> method, we will set the title property to “Employee Data” and redirect the user to “/employee/fetch” page<strong>.</strong></p>
<h3 id="heading-employeedatacshtml">EmployeeData.cshtml</h3>
<p>Open the “EmployeeData.cshtml” page and put the following code into it:</p>
<pre><code>@page <span class="hljs-string">"/employee/{action}/{paramEmpID}"</span>@page <span class="hljs-string">"/employee/{action}"</span>@inherits EmployeeDataModel&lt;h1&gt;@title&lt;<span class="hljs-regexp">/h1&gt;@if (action == "fetch"){    &lt;p&gt;        &lt;a href="/</span>employee/create<span class="hljs-string">"&gt;Create New&lt;/a&gt;    &lt;/p&gt;}@if (action == "</span>create<span class="hljs-string">" || action == "</span>edit<span class="hljs-string">"){    &lt;form&gt;        &lt;table class="</span>form-group<span class="hljs-string">"&gt;            &lt;tr&gt;                &lt;td&gt;                    &lt;label for="</span>Name<span class="hljs-string">" class="</span>control-label<span class="hljs-string">"&gt;Name&lt;/label&gt;                &lt;/td&gt;                &lt;td&gt;                    &lt;input type="</span>text<span class="hljs-string">" class="</span>form-control<span class="hljs-string">" bind="</span>@emp.Name<span class="hljs-string">" /&gt;                &lt;/td&gt;                &lt;td width="</span><span class="hljs-number">20</span><span class="hljs-string">"&gt; &lt;/td&gt;                &lt;td&gt;                    &lt;label for="</span>Department<span class="hljs-string">" class="</span>control-label<span class="hljs-string">"&gt;Department&lt;/label&gt;                &lt;/td&gt;                &lt;td&gt;                    &lt;input type="</span>text<span class="hljs-string">" class="</span>form-control<span class="hljs-string">" bind="</span>@emp.Department<span class="hljs-string">" /&gt;                &lt;/td&gt;            &lt;/tr&gt;            &lt;tr&gt;                &lt;td&gt;                    &lt;label for="</span>Gender<span class="hljs-string">" class="</span>control-label<span class="hljs-string">"&gt;Gender&lt;/label&gt;                &lt;/td&gt;                &lt;td&gt;                    &lt;select asp-for="</span>Gender<span class="hljs-string">" class="</span>form-control<span class="hljs-string">" bind="</span>@emp.Gender<span class="hljs-string">"&gt;                        &lt;option value="</span><span class="hljs-string">"&gt;-- Select Gender --&lt;/option&gt;                        &lt;option value="</span>Male<span class="hljs-string">"&gt;Male&lt;/option&gt;                        &lt;option value="</span>Female<span class="hljs-string">"&gt;Female&lt;/option&gt;                    &lt;/select&gt;                &lt;/td&gt;                &lt;td width="</span><span class="hljs-number">20</span><span class="hljs-string">"&gt; &lt;/td&gt;                &lt;td&gt;                    &lt;label for="</span>City<span class="hljs-string">" class="</span>control-label<span class="hljs-string">"&gt;City&lt;/label&gt;                &lt;/td&gt;                &lt;td&gt;                    &lt;input type="</span>text<span class="hljs-string">" class="</span>form-control<span class="hljs-string">" bind="</span>@emp.City<span class="hljs-string">" /&gt;                &lt;/td&gt;            &lt;/tr&gt;            &lt;tr&gt;                &lt;td&gt;&lt;/td&gt;                &lt;td&gt;                    &lt;input type="</span>submit<span class="hljs-string">" class="</span>btn btn-success<span class="hljs-string">" onclick="</span>@(<span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> CreateEmployee())<span class="hljs-string">" style="</span>width:<span class="hljs-number">220</span>px;<span class="hljs-string">" value="</span>Save<span class="hljs-string">" /&gt;                &lt;/td&gt;                &lt;td&gt;&lt;/td&gt;                &lt;td width="</span><span class="hljs-number">20</span><span class="hljs-string">"&gt; &lt;/td&gt;                &lt;td&gt;                    &lt;input type="</span>submit<span class="hljs-string">" class="</span>btn btn-danger<span class="hljs-string">" onclick="</span>@Cancel<span class="hljs-string">" style="</span>width:<span class="hljs-number">220</span>px;<span class="hljs-string">" value="</span>Cancel<span class="hljs-string">" /&gt;                &lt;/td&gt;            &lt;/tr&gt;        &lt;/table&gt;    &lt;/form&gt;}else if (action == "</span><span class="hljs-keyword">delete</span><span class="hljs-string">"){    &lt;div class="</span>col-md<span class="hljs-number">-4</span><span class="hljs-string">"&gt;        &lt;table class="</span>table<span class="hljs-string">"&gt;            &lt;tr&gt;                &lt;td&gt;Name&lt;/td&gt;                &lt;td&gt;@emp.Name&lt;/td&gt;            &lt;/tr&gt;            &lt;tr&gt;                &lt;td&gt;Gender&lt;/td&gt;                &lt;td&gt;@emp.Gender&lt;/td&gt;            &lt;/tr&gt;            &lt;tr&gt;                &lt;td&gt;Department&lt;/td&gt;                &lt;td&gt;@emp.Department&lt;/td&gt;            &lt;/tr&gt;            &lt;tr&gt;                &lt;td&gt;City&lt;/td&gt;                &lt;td&gt;@emp.City&lt;/td&gt;            &lt;/tr&gt;        &lt;/table&gt;        &lt;div class="</span>form-group<span class="hljs-string">"&gt;            &lt;input type="</span>submit<span class="hljs-string">" class="</span>btn btn-danger<span class="hljs-string">" onclick="</span>@(<span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> DeleteEmployee())<span class="hljs-string">" value="</span>Delete<span class="hljs-string">" /&gt;            &lt;input type="</span>submit<span class="hljs-string">" value="</span>Cancel<span class="hljs-string">" onclick="</span>@Cancel<span class="hljs-string">" class="</span>btn<span class="hljs-string">" /&gt;        &lt;/div&gt;    &lt;/div&gt;}@if (empList == null){    &lt;p&gt;&lt;em&gt;Loading...&lt;/em&gt;&lt;/p&gt;}else{    &lt;table class='table'&gt;        &lt;thead&gt;            &lt;tr&gt;                &lt;th&gt;ID&lt;/th&gt;                &lt;th&gt;Name&lt;/th&gt;                &lt;th&gt;Gender&lt;/th&gt;                &lt;th&gt;Department&lt;/th&gt;                &lt;th&gt;City&lt;/th&gt;            &lt;/tr&gt;        &lt;/thead&gt;        &lt;tbody&gt;            @foreach (var emp in empList)            {                &lt;tr&gt;                    &lt;td&gt;@emp.EmployeeId&lt;/td&gt;                    &lt;td&gt;@emp.Name&lt;/td&gt;                    &lt;td&gt;@emp.Gender&lt;/td&gt;                    &lt;td&gt;@emp.Department&lt;/td&gt;                    &lt;td&gt;@emp.City&lt;/td&gt;                    &lt;td&gt;                        &lt;a href='/employee/edit/@emp.EmployeeId'&gt;Edit&lt;/a&gt;  |                        &lt;a href='/employee/delete/@emp.EmployeeId'&gt;Delete&lt;/a&gt;                    &lt;/td&gt;                &lt;/tr&gt;            }        &lt;/tbody&gt;    &lt;/table&gt;}</span>
</code></pre><p>At the top, we have defined the routes for our page. There are two routes defined:</p>
<ol>
<li><code>/employee/{action}/{paramEmpID}</code> : This will accept the action name along with employee ID. This route is invoked when we perform an Edit or Delete operation<em>.</em> When we call an <code>edit</code> or <code>delete</code> action on a particular employee’s data, the employee ID is also passed as the URL parameter.</li>
<li><code>/employee/{action}</code> : This will only accept the action name. This route is invoked when we create a new employee’s data, or we fetch the records of all the employees.</li>
</ol>
<p>We are also inheriting the<code>EmployeeDataModel</code> class, which is defined in the “EmployeeData.cshtml.cs” file. This will allow us to use the methods defined in the <code>EmployeeDataModel</code> class.</p>
<p>After this, we are setting the title that will be displayed on our page. The title is dynamic and changes as per the action that is being executed currently on the page.</p>
<p>We will show the “Create New” link only if the action is <code>fetch</code>. If the action is <code>create</code> or <code>edit</code> then the “Create New” link will be hidden and we will display the form to get the user input. Inside the form, we have also defined the two buttons “Save” and “Cancel”. Clicking on “Save” will invoke the <code>CreateEmployee</code> method whereas clicking on “Cancel” will invoke the <code>Cancel</code> method.</p>
<p>If the action is <code>delete</code> then a table will be displayed with the data of the employee on which the <code>delete</code> action is invoked. We are also displaying two buttons — “Delete” and “Cancel”. Clicking on the “Delete” button will invoke the <code>DeleteEmployee</code> method, and clicking on “Cancel” will invoke the <code>Cancel</code> method.</p>
<p>At the end, we have a table to display all the employee data from the database. Each employee record will also have two action links: “Edit” to edit the employee record and “Delete” to delete the employee record. This table is always displayed on the page, and we will update it after performing every action.</p>
<h3 id="heading-adding-the-link-to-the-navigation-menu">Adding the link to the Navigation menu</h3>
<p>The last step is to add the link to our “EmployeeData” page in the navigation menu. Open the “BlazorSPA.Client/Shared/NavMenu.cshtml” page and put the following code into it:</p>
<pre><code>&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"top-row pl-4 navbar navbar-dark"</span>&gt;    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>BlazorSPA<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">@ToggleNavMenu</span>&gt;</span>        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler-icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>&lt;<span class="hljs-regexp">/div&gt;&lt;div class=@(collapseNavMenu ? "collapse" : null) onclick=@ToggleNavMenu&gt;    &lt;ul class="nav flex-column"&gt;        &lt;li class="nav-item px-3"&gt;            &lt;NavLink class="nav-link" href="/</span><span class="hljs-string">" Match=NavLinkMatch.All&gt;                &lt;span class="</span>oi oi-home<span class="hljs-string">" aria-hidden="</span><span class="hljs-literal">true</span><span class="hljs-string">"&gt;&lt;/span&gt; Home            &lt;/NavLink&gt;        &lt;/li&gt;        &lt;li class="</span>nav-item px<span class="hljs-number">-3</span><span class="hljs-string">"&gt;            &lt;NavLink class="</span>nav-link<span class="hljs-string">" href="</span>/employee/fetch<span class="hljs-string">"&gt;                &lt;span class="</span>oi oi-list-rich<span class="hljs-string">" aria-hidden="</span><span class="hljs-literal">true</span><span class="hljs-string">"&gt;&lt;/span&gt; Employee data            &lt;/NavLink&gt;        &lt;/li&gt;    &lt;/ul&gt;&lt;/div&gt;@functions {    bool collapseNavMenu = true;    void ToggleNavMenu()    {        collapseNavMenu = !collapseNavMenu;    }}</span>
</code></pre><p>Hence, we have successfully created an SPA using Blazor, with the help of the Entity Framework Core database first approach.</p>
<h3 id="heading-execution-demo">Execution demo</h3>
<p>Launch the application.</p>
<p>A web page will open as shown in the image below. The navigation menu on the left is showing the navigation link for the Employee data page.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/5aytCUZiGENxmbG8UmH3Kkn93L6QlmU3o90I" alt="Image" width="650" height="377" loading="lazy"></p>
<p>Clicking on the “Employee data” link will redirect to the “Employee Data” view. Here you can see all the employee data on the page. Notice the URL has “employee/fetch” appended to it.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/e1LB246vIs1UwwYqU4hb7kgEqktwtXg6QxIW" alt="Image" width="650" height="295" loading="lazy"></p>
<p>We have not added any data, hence it is empty. Click on “CreateNew” to open the “Add Employee” form to add new employee data. Notice the URL has “employee/create” appended to it:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/GhLUT-gjoQoOLHv8ldQElEqINjpFE-YoCyd4" alt="Image" width="650" height="346" loading="lazy"></p>
<p>After inserting data in all the fields, click on the “Save” button. The new employee record will be created, and the Employee data table will get refreshed.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/VkPKhxGiT5Pg9aEjgZtsSiSqFdaT3fUnosFe" alt="Image" width="650" height="346" loading="lazy"></p>
<p>If we want to edit an existing employee record, then click on the “Edit” action link. It will open Edit view as shown below. Here we can change the employee data. Notice that we have passed the employee ID in the URL parameter.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/a83joAzqvLOvLkrBphgo1X1iNMCJZX4h3hW1" alt="Image" width="650" height="400" loading="lazy"></p>
<p>Here we have changed the City of employee Swati from Mumbai to Kolkatta. Click on “Save” to refresh the employee data table to view the updated changes as highlighted in the image below:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/YTVLVrodvmccRgROnCHeUoYjJDc4gCYo9ma0" alt="Image" width="650" height="400" loading="lazy"></p>
<p>Now we will perform a Delete operation on the employee named Dhiraj. Click on the “Delete” action link, which will open the Delete view asking for a confirmation to delete. Notice that we have passed the employee ID in the URL parameter.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/vh9OcLX0TZBjpK4FZZRgNdULvxM45sPSZfBq" alt="Image" width="650" height="484" loading="lazy"></p>
<p>Once we click on the “Delete” button, it will delete the employee record and the employee data table will be refreshed. Here, we can see that the employee with name Dhiraj has been removed from our record.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/6GYVO2fxT7YUq9pkt5taPVC9dCtTQOlnPnMj" alt="Image" width="650" height="318" loading="lazy"></p>
<h3 id="heading-deploying-the-application">Deploying the application</h3>
<p>To learn how to deploy a Blazor application using IIS, refer to <a target="_blank" href="http://ankitsharmablogs.com/deploying-a-blazor-application-on-iis/">Deploying A Blazor Application On IIS</a>.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>We have created a Single Page Application with Razor pages in Blazor using the Entity Framework Core database first approach with the help of Visual Studio 2017 and SQL Server 2014. We have also performed the CRUD operations on our application.</p>
<p>Please get the source code from <a target="_blank" href="https://github.com/AnkitSharma-007/SPA-With-Blazor">GitHub</a> and play around to get a better understanding.</p>
<p>Get my book <a target="_blank" href="https://www.amazon.com/Blazor-Quick-Start-Guide-applications/dp/178934414X/ref=sr_1_1?ie=UTF8&amp;qid=1542438251&amp;sr=8-1&amp;keywords=Blazor-Quick-Start-Guide">Blazor Quick Start Guide</a> to learn more about Blazor.</p>
<p>You can also read this article at <a target="_blank" href="https://www.c-sharpcorner.com/article/creating-a-spa-using-razor-pages-with-blazor/">C# Corner</a></p>
<p>You can check my other articles on Blazor <a target="_blank" href="http://ankitsharmablogs.com/category/blazor/">here</a>.</p>
<h3 id="heading-see-also">See Also</h3>
<ul>
<li><a target="_blank" href="http://ankitsharmablogs.com/asp-net-core-getting-started-with-blazor/">ASP.NET Core — Getting Started With Blazor</a></li>
<li><a target="_blank" href="http://ankitsharmablogs.com/asp-net-core-crud-using-blazor-and-entity-framework-core/">ASP.NET Core — CRUD Using Blazor And Entity Framework Core</a></li>
<li><a target="_blank" href="http://ankitsharmablogs.com/cascading-dropdownlist-in-blazor-using-ef-core/">Cascading DropDownList In Blazor Using EF Core</a></li>
<li><a target="_blank" href="https://www.c-sharpcorner.com/article/razor-page-web-application-with-asp-net-core-using-ado-net/">Razor Page Web Application With ASP.NET Core Using ADO.NET</a></li>
<li><a target="_blank" href="http://ankitsharmablogs.com/asp-net-core-crud-using-angular-5-and-entity-framework-core/">ASP.NET Core — CRUD Using Angular 5 And Entity Framework Core</a></li>
<li><a target="_blank" href="http://ankitsharmablogs.com/asp-net-core-crud-with-react-js-and-entity-framework-core/">ASP.NET Core — CRUD With React.js And Entity Framework Core</a></li>
</ul>
<p>Originally published at <a target="_blank" href="https://ankitsharmablogs.com/">https://ankitsharmablogs.com/</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
