Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Sunday, 30 January 2022

local mock data for SPA web service

Whenever I build SPA's in vue I use a poor mans IOC pattern, which in principle accomplishes leveraging a mock version of my service that instead of fetching data from a web api leverages local json files, however in practice it has very little to do with Inversion of control, though it could adhere to IOC principles I find my pragmatic approach less hassle. 

I start with creating env files: moq, dev, prod

.env.dev

VUE_APP_ENV=dev
VUE_APP_HEADER_COLOR=#9F1F36

.env.moq

VUE_APP_ENV=moq
VUE_APP_HEADER_COLOR=#FFD200

.env.prod

VUE_APP_ENV=prod
VUE_APP_HEADER_COLOR=

as you can see I also like to add a a color code so that i can easily tell which environment I am currently working on.

next I create a services folder where I keep all my logic that communicates outside my app and a mock_data folder where I keep json files that will represent the responses that I would get from my backend api.


so let's create a simple json file for our mock data, here i have created a openings.json file that contains three chess openings

[
  {
    "description""Sacrifice a pawn for a head start",
    "id""55d86e6f-b043-4544-ae6b-b88ee4ee134b",
    "name""King's gambit",
    "tags": ["kingsPawn""gambit"]
  },
  {
    "description""trade a pawn for central occupation",
    "id""52366eb7-fcad-4b58-9ad3-f73c1585c16a",
    "name""Queens's gambit",
    "tags": ["queensPawn""gambit"]
  },
  {
    "description""Win by oversight",
    "id""e5f7f895-073c-4a24-9df0-d9d65b6ad46a",
    "name""Scholar's mate",
    "tags": ["kingsPawn""dubious"]
  }
]

this would be the payload i would expect from some web api get openings, but for now we will just use local mock data.

**in your tsconfig.json and make sure to add "resolveJsonModule": true to the compilerOptions

next create a openingsService.ts file in which you should define an interface and two services a private mock service and a default prod/dev service. however rather than creating a context and pass that into the service service what i do is use the object.assign function to override my dev/prod services with a mock counter part.

import { IOpening } from "@/models/opening";

export interface IOpeningService {
  getOpeningsAsync: () => Promise<IOpening[]>
}

class MoqOpeningService implements IOpeningService {
  getOpeningsAsync: () => Promise<IOpening[]> = 
    async () => {
      return require<IOpening[]>("@/mock_data/openings.json");
    };
}

export default class OpeningService implements IOpeningService {
  constructor() {
    if(process.env.VUE_APP_ENV === "moq")
      Object.assign(thisnew MoqOpeningService());
  }

  getOpeningsAsync: () => Promise<IOpening[]> = 
    async () => {
      throw new Error("getOpeningsAsync not implemented");
    }
}

notice that in the constructor of the default class there is a check of which environment is running and if it is the "moq" version a new instance of the MoqOpeningService is assigned to the current opening service overriding everything defined in the interface. 

now that is fine and dandy however we have one final caveat, all of your mock data is going to be packaged in your dist folder when you build your project.

To exclude mock files from your build 

firstly you have to add a reference to the webpack npm package 

i used the 4.45.0 version because I was getting exceptions with the latest major 5+ version

npm install webpack@^4.45.0 --save-dev

with webpack install if you haven't already created a vue.config.js file go a head and do that at the root of your project, refer to the project structure screen capture at the start of this post.

//vue.config.js

// eslint-disable-next-line @typescript-eslint/no-var-requires
const webpack = require('webpack')

module.exports = {  
  configureWebpack: {
    plugins: process.env.VUE_APP_ENV == "prod" ? [
      new webpack.IgnorePlugin({
        resourceRegExp: /mock_data\/.*\.json$/,
        contextRegExp: /services$/
      })
    ] : []
  }
}

IgnorePlugin | webpack

now when you build your project your mock data will not be included in your dist folder.


Tuesday, 5 January 2021

Create a "Simple" webpack project

If you ever find a developer hiding in a closet head in hands and tears running down his or her face, you can be sure that webpack did it to them. Web pack is this omnipresent monster that most front-end developers don't know is lurking in the shadows, so close that it can touch you, so near that it can feel your heartbeat. well in this post we are going to pull out that flashlight and shine a light under the bed to expose webpack for the pussy cat it is rather than the hound of hell it's been made out to be.

let's start by creating a directory for our project, i went with pav.wpbase because i go by pav and wpbase stands for "winnie the pooh bear abolishes sour elephants"... no wait it stands for webpack base

mkdir pav.wpbase


any way next enter your directory and initialize a npm project

cd pav.wpbase

npm init

once you initialize your npm project you'll be asked a bunch of config questions, your answers wont really matter but they will be used to initialize your package.json file, the only thing of consequence that i changes was the entry point to the application, however you can always do that after in the actual package.json file.


now with that done if you do a dir of the directory you should have a package.json file

if you open that file it should look very familiar to you


{
  "name""pav.wpbase",
  "version""1.0.0",
  "description""base webpack project",
  "main""src/index.js",
  "scripts": {
    "test""echo \"Error: no test specified\" && exit 1"
  },
  "author""pav",
  "license""ISC"
}

exactly what you entered above during your npm interrogation.

anyway next we obviously need to bring in webpack and a whole bunch of other packages so run the following command

npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin copy-webpack-plugin babel-loader style-loader sass-loader sass  @babel/core @babel/preset-env webpack

now to break that down 

npm i -D install dev dependencies 
   webpack: bundles all of your assets into 
   webpack-cli: lets you use the webpack command line interface ie npx commands
   webpack-dev-server: lets you run a localhost server
   html-webpack-plugin: lets you copy you html file to your dist folder
   copy-webpack-plugin: lets you copy files from your working directory to your dist folder
   @babel/core
   @babel/preset-env webpack 
   babel-loader: lets us write backwards/cross browser compatible code
   css-loader: translates css to common js
   style-loader: Creates style nodes from JS strings
   sass-loader: Complies your scss to css
   sass: lets you write sass instead of basic css

now that those are referenced make sure to run the npm install command this will actually pull down the referenced packages to be used in your project.

now let's take a look at our package.json file one more time


{
  "name""pav.wpbase",
  "version""1.0.0",
  "description""base webpack project",
  "main""src/index.js",
  "scripts": {
    "test""echo \"Error: no test specified\" && exit 1"
  },
  "author""pav",
  "license""ISC",
  "devDependencies": {
    "@babel/core""^7.15.5",
    "@babel/preset-env""^7.15.6",
    "babel-loader""^8.2.2",
    "copy-webpack-plugin""^9.0.1",
    "css-loader""^6.3.0",
    "html-webpack-plugin""^5.3.2",
    "sass""^1.42.1",
    "sass-loader""^12.1.0",
    "style-loader""^3.3.0",
    "webpack""^5.56.1",
    "webpack-cli""^4.8.0",
    "webpack-dev-server""^4.3.1"
  }
}

and as you can see we reference all of the packages that we are going to need for our "simple" webpack project.

next lets create our src folder structure, this is where we are going to code up our website, now this is mostly a matter of preference; there are some conventions that are followed but not religiously, so figure out what works for you are just copy somebody else's and remember you can always refactor.



Now that's the simple structure that I go with.

Next let's create a webpack.config.jsfile at the root of the project (the same level as your src folder), this is where the terror I mean magic happens. so let's start with a very simple implementation, just enough to build a dist folder and create a bundle.js with a index.html page 


const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/scripts/index.js',
  output: {
    path: __dirname + '/dist',
    filename: "bundle.js"
  },
  plugins:[
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    })
  ]
}


Now let's go back into our command line and run the command 
npx webpack build
this command will use our webpack config file to create a dist folder; after running the above command take a look at our project


pretty cool, we now have an index.html based on what we did in our src folder, and a bundle.js that is based on our index.js file and anything that it references. 

now let's start with something simple open up your html in your src folder and add some content.


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>wpbase</title>
</head>
<body>
  <header>
    <h1>Hello world</h1>
  </header>
  <section>section 1</section>
  <section>section 2</section>
  <section>section 3</section>
  <section>section 4</section>
  <section>section 5</section>
  <section>section 6</section>
  <footer>Goodbye world</footer>
</body>
</html>


now let's run our build command again 

npx webpack build and if we open the index.html file in our dist folder we'll see the following




the exact changes we made and if you notice the URL it is in fact being served from our dist folder, now before we continue let's set up a script in our package.json file so that we don't have to use npx


{
  "name""pav.wpbase",
  "version""1.0.0",
  "description""base webpack project",
  "main""src/scripts/index.js",
  "scripts": {
    "serve""webpack serve",
    "test""echo \"Error: no test specified\" && exit 1",
    "build""webpack build"
  },
  "author""pav",
  "license""ISC",
  "devDependencies": {
    "@babel/core""^7.15.5",
    "@babel/preset-env""^7.15.6",
    "babel-loader""^8.2.2",
    "copy-webpack-plugin""^9.0.1",
    "css-loader""^6.3.0",
    "html-webpack-plugin""^5.3.2",
    "sass""^1.42.1",
    "sass-loader""^12.1.0",
    "style-loader""^3.3.0",
    "webpack""^5.56.1",
    "webpack-cli""^4.8.0",
    "webpack-dev-server""^4.3.1"
  }
}


notice the scripts section, we added two more script run and build, these are our scripts that we execute on an npm run <<script name>> so in our cases

npm run build
npm run serve

both commands will work however the latter will not have live update, that is once the server starts it will not update with any changes we make, but let's fix that; back to the webpack.config.js file


const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/scripts/index.js',
  output: {
    path: __dirname + '/dist',
    filename: "bundle.js"
  },
  plugins:[
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    })
  ],
  devtool: 'source-map',
  devServer: {
    static: {                               
      directory: path.join(__dirname'./'),  
      watch: true
    }
  }
}


notice the last two properties in our config, devTool: this will allow for friendlier debugging, so when you you console logs the debugger will let you know the originating js file rather than just the bundled one. as for the second property devServer, this loads your src file into memory and lets your local host serve it via the browser, also the watch property tells our server to update any time our source code changes.

this is huge, this will drastically speed up our workflow.

next let's configure our scss, because who writes basic css anymore. so let's again open up our webpack.config.js file and add a rule


const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/scripts/index.js',
  output: {
    path: __dirname + '/dist',
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // 3 Creates `style` nodes from JS strings
          "style-loader",
          // 2 Translates CSS into CommonJS
          "css-loader",
          // 1 Compiles Sass to CSS
          "sass-loader",
        ]
      }
    ]
  },
  plugins:[
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    })
  ],
  devtool: 'source-map',
  devServer: {
    static: {                               
      directory: path.join(__dirname'./'),  
      watch: true
    }
  }
}


notice the new module section with an array of rules. first we test for files that end in a sass extension if a file ends in a sass extension we then run it through our pipeline, where we start by converting sass to scss then css into common js then finally we move that js into our js bundle

to test this lets create two scss files base.scss and header.scss

in our base.scss 

$primaryColor#F0f;


next in our header.scss

@import 'base';

header{
  h1 {
    color:$primaryColor
  }
}


and finally in our index.js
import "../styles/header.scss";

now for our scss files to be bundled they have to be referenced in our js file. 

make sure to hit ctrl+c to stop your web server and npm run serve to restart it so that your new webpack.config file takes effect.

and voilia we have the following 

we're successfully compiling our styles into our bundle.js file, if you build your project using npm run build you'll find something like the following in bundle.js


// Module
___CSS_LOADER_EXPORT___.push([module.id"h1 {\n  color: #F0f;\n}"
"",{"version":3,"sources":["webpack://./src/styles/header.scss",
"webpack://./src/styles/base.scss"],"names":[],"mappings":
"AAEA;EACE,WCHa;ADEf","sourcesContent":["@import 'base';\r\n\r\nh1 
{\r\n  color:$primaryColor\r\n}\r\n","$primaryColor: #F0f;\r\n"],
"sourceRoot":""}]);

 
which is a js compiled version of our scss

now for the final step let's include our babel to make our code backwards compatible, again you guessed it back to the webpack.config.js file


const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/scripts/index.js',
  output: {
    path: __dirname + '/dist',
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          // 3 Creates `style` nodes from JS strings
          "style-loader",
          // 2 Translates CSS into CommonJS
          "css-loader",
          // 1 Compiles Sass to CSS
          "sass-loader",
        ]
      }
    ]
  },
  plugins:[
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    })
  ],
  devtool: 'source-map',
  devServer: {
    static: {                               
      directory: path.join(__dirname'./'),  
      watch: true
    }
  }
}


and again we added a rule this time to use the babel-loader. next we have to create a .babelrc file at our project root.

{
  "presets": ["@babel/preset-env"]
}

simple enough, 99 times out of 100 this will suffice

and that's it, now our JavaScript is cross browser and backwards compatible to the best of babel's ability.

Friday, 17 July 2020

MSAL localhost

So let's say that you have MSAL configured and it works perfectly in production however it fails in your development (localhost) environment. As a matter of fact you get a javascript error message such as the following.

Unsafe JavaScript attempt to initiate navigation for frame with origin 'https://myurl.azurewebsites.net' from frame with URL 'http://localhost:8080/login'. The frame attempting navigation is neither same-origin with the target, nor is it the target's parent or opener.

well my dear friend odds are you most likely made the same bonehead move that I did, especially if on your older laptop it worked perfectly, but fails on your new one. I'll cut to the chase, you need to specify the correct redirect url when defining your IdentityContext:

  IdentityServicenew IdentityService(
    new IdentityContext(
      process.env.VUE_APP_CLIENT_ID, 
      process.env.VUE_APP_REDIRECT_URL,                   
      `https://login.microsoftonline.com/${process.env.VUE_APP_TENANT_ID}`))

as you can most like deduce i am using vue.js environment variables to hold my values, but you know which files do not get pushed to your repo, the ones that end in .local, and which ones does your local machine use? the ones that end in .local. 

trust me if this solved your problem you are feeling a lot less silly than I did when I figured it out.

all i had to do was open my .env.dev file

VUE_APP_TITLE=DEV-remote
VUE_APP_CLIENT_ID=00000000-957e-4ae1-a64a-ab186e161727
VUE_APP_REDIRECT_URL=https://myurl.azurewebsites.net
VUE_APP_PROFILE_MATCHCER_API_URL=https://myurl.azurewebsites.net/api/v1

and change it to

VUE_APP_TITLE=DEV-remote
VUE_APP_CLIENT_ID=00000000-957e-4ae1-a64a-ab186e161727
VUE_APP_REDIRECT_URL=https://localhost:8080
VUE_APP_PROFILE_MATCHCER_API_URL=https://myurl.azurewebsites.net/api/v1

and all was well in the world again

Friday, 15 November 2013

Canadian Sin Regex

Regex is a two fold problem:

  • the first is that you have a problem
  • the second is that your problem can be solved using regex

What do I mean by this? well regex is one of those things that you're not going to use very often so it's not really worth the effort to learn it 100% but just enough to get what you need done and forget it by the time you need it again, much like calculus.

Anyway this is a pretty solid site to find what you need Regex Library.

but this is what I use to verify a Canadian sin "^\d{3}(\s?|[-]?)\d{3}(\s?|[-]?)\d{3}$"

It's got you covered for 123123123, 123 123 123, 123-123-123

to use it with an input box use a combination of the pattern and title attributes

<input id = "SIN_TXT" name="SIN_TXT" type ="text" title="6 digit Canadain Social Insurence Number"  pattern="^\d{3}(\s?|[-]?)\d{3}(\s?|[-]?)\d{3}$" placeholder="### ### ###" />

if you are a poor soul that has to support a lower end browser such as ie8 then attach it using javascript

function validateSIN(sinString)
{
    var sinPattern = /^\d{3}(\s?|[-]?)\d{3}(\s?|[-]?)\d{3}$/;
    return sinPattern.test(sinString);
}

Now that you've validated that the user is submitting a 9 digit number, lets confirm that it is in fact a SIN and not just 9 random digits, check out this Validate Sin Number.

here's the algorithm I use:

function verifySIN(SIN) {
    var sinArray = SIN.replace(/\W/g, '')
    var t = 0;
    if (sinArray.length != 9)
        return false;

    for (x in sinArray) {
        var p = 0;
        t += (p = (x % 2 + 1) * sinArray[x]) > 9 ? (p + (p % 10 * 9)) / 10 : p;
    }

    return t % 10 === 0;
}

Saturday, 1 December 2012

Validate & Verify a Date

When I say Verify, I mean make sure that it's in a correct format; this can very easily be accomplished using regex; if you don't know what that is, it's basically an expression to check formatting of a string against. I suggest reading the following site:

Zytrax this is an excellent source for regex knowledge, I use it regularly.

The following JavaScript function takes in a string and checks it against the following pattern ##/##/####. you may notice it doesn't make sure that the month is between 1-12 or the day is less then 31, it just makes sure that you entered 3 numbers and that they're separated by forward-slashes.

function validdateDate(dateString)
{
    var datePattern = /^\d{2}[/]\d{2}[/]\d{4}$/;
    return datePattern.test(dateString);
}

Now Validate, well that's a bit more tricky. First lets talk about what I mean by validate, if you run the date 02/29/2011 against a simple date Regex it will come back as valid, but it's clearly not since that date never occurred.

At first I thought I could just load the individual month day year into a constructor for a JavaScript Date object and just get an invalid date exception, but no such luck, lovely JavaScript  will just bump the date up to 03/01/2011. Good bad it doesn't really matter, what matters is that I still haven't validated my Date. It's high school programming time.

To resolve this issue first we need a function to check if we're dealing with a leap year, simple enough you can grab the algorithm from numerous sites; I got this one from Wikipedia or if your super lazy it's below.

function isLeapYear(year)
{
    if(year%400==0)
        return true;
    else if(year%100==0)
        return false;
    else if(year%4==0)
        return true;
    return false;
}

Now that we can tell if we're dealing with a leap year lets check to make sure that our date is an actual date. that has occurred.

function verifyDate(dateString)
{
    var daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];

    if(dateString.length != 10)
        return false;

    var da = dateString.split("/");
  
    if(da[2] <= new Date().getFullYear())
    {
        if(da[0] > 0 && da[0] < 13)
        {
            if(da[0] == 2 && isLeapYear(da[2]))
                return (da[1] > 0 && da[1] < 30);
            else
                return (da[1] > 0 && da[1] < daysInMonth[da[0]-1]);
        }
    }
    return false;
}

Well there you have it, are there other much more robust ways to check date, sure there are, could I have written this function to take in some sort of pattern to check against rather then just month/day/year, absolutely, but i'm not writing an API, I'm just making a one off function to filter out shitty data before it hits my server.

Friday, 16 November 2012

Simple jQuery & JavaScript Bubble Sort

jQuery is pretty powerful, and you should leverage it as much as possible. Someone might say but what if the client has javascript disabled? My response is simple, they don't even deserve to sort. anyway here's a quick example.

Here's the HTML

<html>
  <head>
    <title>Date Example</title>
    <script src = "jquery-1.6.1.js"></script>
    <script src = "script.js"></script>
  </head>
  <body>
    <input type = "button" value = "sort" onclick="sort();" />
    <div>Nov 12, 2012 1:00 PM EST</div>
    <div>Oct 09, 2012 5:00 PM EDT</div>
    <div>Nov 29, 2012 2:00 PM EST</div>
    <div>Oct 09, 2012 5:00 PM EDT</div>
    <div>Nov 09, 2012 2:00 PM EST</div>
    <div>Nov 19, 2012 2:00 PM EST</div>
    <div>Nov 09, 2012 2:00 PM EST</div>
    <div>Nov 29, 2012 2:00 PM EST</div>
    <div>Nov 05, 2012 12:00 AM EST</div>
    <div>Nov 08, 2012 9:10 AM EST</div>
  </body>
</html>

and followed by the JQuery

function sort()
{
  var swap = false
  var prev = null;
 
  do {
    prev = null;
    swap = false;
    $('div').each(
    function (indexelement)
    {
      if(prev != null && shouldSwap(prev,element))
      {
        $(this).after($(prev));
        swap = true;
      } 
  
      prev = element;
    });
  }while(swap)
}

function shouldSwap(d1d2)
{
  var dateOne = new Date($(d1).text());
  var dateTwo = new Date($(d2).text());
 
  return dateOne > dateTwo;
}

or if you prefer vanilla JavaScript (which I now do, since I haven't written JQuery in years)

function sort() {
  let swap = false
  let prev = null;
  let predicate = (d1d2=> new Date(d1) > new Date(d2);
  
  do { 
    prev = null;
    swap = false;
    const divs = [...document.getElementsByTagName("div")];
    
    divs.forEach((elementindex=> {
        if(prev != null && predicate(prev.innerText, element.innerText)) {
          element.after(prev);
          swap = true;
        } 
        prev = element;
   });
 } while(swap)
}

now you may notice this is not the most efficient bubble sort, it's a simple example to prove a point not efficiently sort 1'000'000 records.