Categories
Software Development Web Development

How to get URL Routes in your Javascript in Symfony 5+

This is something I needed to know how to do so I could generate URLs inside my javascript more easily.

Currently the docs on this are outdated so I will record what I did here so I can know later, or for others who need this info.

The docs say something about app/appKernel.php that file no longer exists.  It has been replaced with just kernel.php and you do pretty much nothing to that file.

I am not sure what version Symfony started this but I know in 5+ you don’t have to register the bundle like step 2 of the old docs shows. This is done automatically for you in a new file named config/bundles.php which is where all bundles are automatically registered when you install them.

So all you do is this command now in the terminal

composer require friendsofsymfony/jsrouting-bundle

inside your apps root level. It is auto registered for you.

You don’t have to do step 3 of the old docs either as the config system has changed. There is no

app/config/routing.yml

file anymore. When you install the bundle the new system creates a new file named app/config/routes/fos_js_routing.yaml for you
For step 4 the required code has changed you need to enter this now.

$ php bin/console assets:install --symlink public

Basically to install the bundle you just use the composer command and most of the stuff is now done for you.

A silent secret.

In order for the routes to work you must add something new to every single route definition you want to be able to generate a route in your javascript for.

You must add this to the annotation or where ever you define your routes. I use annotations so I can just look to the controllers, plus when I split the system into microservices the routes go with their services.

options={"expose"=true}

So for my menu route the definition in the controller looks like this.

@Route("/menu", name="menu", options={"expose"=true}, methods={"GET"})
</code.

Now how to use it?

So now that it is installed how do you use it? One more step. Now you must include the needed Javascript in your page with these tags.

<script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script>
<script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>

Place those anywhere you want. What I did is put them in my base twig template in a section where I include my Javascript this way it is available on every page because I will probably be needing access to routes as I build the app.

Once you have all of the above done you can use it like this

let testUrl = Routing.generate( 'menu');
console.log(testUrl); //outputs /menu

Route not found errors?

Yes I got these too. You must add expose

options={"expose"=true}

to every single route you want to use. And your IDE probably won’t be very helpful so double check the spellings. Routing probably won’t be found by your IDE.

Here is a link to my favorite HTMLEntities converter if you write articles about programming you will need this.

 

Categories
Software Development Web Development

How to create and use a custom Javascript Event

You have probably used events in Javascript many times.  Especially if you have done any User Interface programming. There are many types of events provided by browsers and the Javascript engines.

Did you know you can create your own custom events with the CustomEvent() constructor? Here is a minimal example. In this article I will explain custom events and how to use them.

First off what would you use a custom event for? You use them to notify other objects in your app that actions have occurred such as “user clicked x” or “user closed dialog”. Using events prevents code coupling and reduces dependencies.

I came across this need when creating a dialog box where I wanted an overlay to show beneath it blocking out the page behind. I didn’t want my DialogBox to have to know about my Overlay.  I didn’t want them coupled. I didn’t want my DialogBox to have to have an Overlay object as an argument creating a dependency. I didn’t want my DialogBox to even know that an Overlay object existed.

The answer is for my DialogBox to emit/create/dispatch a custom Javascript Event and have a listener for that event to close the Overlay.

So lets look at some code.


import {Utils} from "./Utils";

class DialogBox {

    /**
     *
     * @param {string} dialogId
     * @param {string} dialogClass
     */
    constructor(dialogId = 'dialogId', dialogClass = '') {
        this.closeDialogId = 'closeDialog';
        this.dialogBoxBottomRowId = 'dialogBoxBottomRow';
        this.dialogBoxClass = 'dialogBox ' + dialogClass;
        this.contentContainerId = 'dialogContentContainer';
        this.dialogHtml = '';
        this.dialogBoxId = dialogId;
        this.closeHandler = null;
        this.divElement = null;
    }

    /**
     *
     * @param {string} rowContent : the content HTML etc to be added as a row
     * @param {string} cssClass : applied only if passed in
     * @returns {void}
     */
    bottomRow(rowContent = '', cssClass = 'dialog-bottom-row') {

        let bottomRow = document.getElementById(this.dialogBoxBottomRowId);

        //if the bottom row does not exist add it to the html
        if ( Utils.isEmpty(bottomRow)) {
            let rowHtml = '<div id="' + this.dialogBoxBottomRowId + '" ';
            rowHtml += ' class="' + cssClass + '" ';
            rowHtml += ' >' + rowContent + '</div>';
            this.dialogHtml += rowHtml;
        } else {
            //if a bottom row exists replace it
            bottomRow.className = cssClass;
            bottomRow.innerText = rowContent;
        }
    }
    /**
     * Centers the dialog vertically and horizontally in the parent element
     * @param {string} parentElementId
     */
    centerDialog(parentElementId = 'body') {

        let parentWidth = 0;
        let parentHeight = 0;
        //need the dialog boxes calculated width and height
        let dialogHeight = this.divElement.clientHeight;
        let dialogWidth = this.divElement.clientWidth;

        //must use two different ways to get the height and width
        if (parentElementId === 'body') {
            parentWidth = window.innerWidth;
            parentHeight = window.innerHeight;
        } else {
            let parentElement = document.getElementById(parentElementId);
            //make sure null or undefined were not returned
            if (!Utils.isEmpty(parentElement)) {
                parentHeight = parentElement.clientHeight;
                parentWidth = parentElement.clientWidth;
            }
        }
        let left = (parentWidth / 2) - (dialogWidth / 2);
        let top = (parentHeight / 2) - (dialogHeight / 2);
        //must add px or it doesn't work at all
        this.divElement.style.top = top + 'px';
        this.divElement.style.left = left + 'px';
    }
    /**
     *
     * @param {string} content
     * @param {string} containerClass
     */
    contentContainer(content, containerClass = 'dialog-content') {
        let contentDiv = document.getElementById(this.contentContainerId);

        //if the container exists replace it contents
        if (Utils.isEmpty(contentDiv)) {
            let contentHtml = '<div id="' + this.contentContainerId + '"';
            contentHtml += ' class="' + containerClass + '" >';
            contentHtml += content + '</div>';
            this.dialogHtml += contentHtml;
        } else {
            contentDiv.innerHTML = content;
            contentDiv.className = containerClass;
        }
    }
    /**
     * calls removeDialogBox which removes the dialog and event listeners
     */
    hideDialogBox() {
        this.removeDialogBox();
    }

    /**
     *
     * @param {string} menuText
     * @param {string} menuTextClass
     */
    menuBar(menuText, menuTextClass = '') {
        let menuTextId = 'dialog-menu-text';
        /*
         * if dialogMenuBar is present then the length will be non zero or true
         * if this is the case replace the content, this allows this method to be called
         * again later to change the value
         */
        let menuBarID = 'dialog-menu-bar';
        let menuTextDiv = document.getElementById(menuTextId);

        if (Utils.isEmpty(menuTextDiv)) {
            this.dialogHtml = '<div class="dialog-menu-bar" id="dialog-menu-bar" >';
            this.dialogHtml += '<div id="' + menuTextId + '" class="dialog-menu-text ';
            this.dialogHtml += menuTextClass + '" >' + menuText + '</div>';
            this.dialogHtml += '<div id="' + this.closeDialogId + '" class="close-dialog" >';
            this.dialogHtml += '<img src="/images/drawing/close-window.png" ';
            this.dialogHtml += 'alt="Close dialog" >';
            this.dialogHtml += '</div></div>';
        } else {
            menuTextDiv.innerText = menuText;
            menuTextDiv.className = menuTextClass;
        }
    }

    /**
     * displays the dialog box, you must call centerDialog to center it
     */
    showDialogBox() {
        //remove any existing dialog boxes first
        this.removeDialogBox();
        this.divElement = document.createElement("div");
        this.divElement.id = this.dialogBoxId;
        this.divElement.className = this.dialogBoxClass;
        this.divElement.innerHTML = this.dialogHtml;
        //position the dialog box now give it the highest z-index to be on top
        this.divElement.style.zIndex = Utils.getHighestZIndex() + 1;
        document.body.appendChild(this.divElement);
        //add the listener for when the user clicks to close

        this.closeHandler = function ( ) {
        this.removeDialogBox();
        }.bind(this);

        let close = document.getElementById(this.closeDialogId);
        close.addEventListener('click', this.closeHandler, false);
    }

    /**
     * removes the dialog html from the page and removes the close listener
     * dispatches event 'dialogClosed' to be used to close an overlay etc.
     */
    removeDialogBox() {
        let dialogElem = document.getElementById(this.dialogBoxId);
        //if a dialog box of the same id exists delete it first to prevent errors and issues
        if (dialogElem) {
            let close = document.getElementById(this.closeDialogId);
            close.removeEventListener('click', this.closeHandler, false);
            dialogElem.remove();
            const dialogEvent = new CustomEvent('dialogClosed');
            document.body.dispatchEvent(dialogEvent);
        }
    }
}

export {DialogBox}

That is a lot of code 169 lines to be exact. I am still in the process of converting this code, still going to add some Template literals instead of the old fashioned string concatenation technique.

There are several very import things to note here in this code. For example the way the addEventListener() and removeEventListener are used. These functions have to be passed THE EXACT SAME parameters or removeEventListener() fails to remove the event listener and that clutters your memory up because you will have listeners referring to elements that don’t exist.

That is why I have this code this.closeHandler


//add the listener for when the user clicks to close

        this.closeHandler = function ( ) {
        this.removeDialogBox();
        }.bind(this);

        let close = document.getElementById(this.closeDialogId);
        close.addEventListener('click', this.closeHandler, false);

See this.closeHandler = function  that stores the function to handle the click on the close button. Both the add and remove event listener functions have to be passed the exact same function.

Look at the removeDialogBox function closer.


let dialogElem = document.getElementById(this.dialogBoxId);
        //if a dialog box of the same id exists delete it first to prevent errors and issues
        if (dialogElem) {
            let close = document.getElementById(this.closeDialogId);
            close.removeEventListener('click', this.closeHandler, false);
            dialogElem.remove();
            const dialogEvent = new CustomEvent('dialogClosed');
            document.body.dispatchEvent(dialogEvent);
        }

Notice that the second argument to removeEventListener is this.closeHandler that is the same function passed to the addEventListener above.

If you use anonymous functions inside add and remove event listeners instead, then they wont be the same function and so your event listener won’t be removed and your memory fills up faster.

Another important note is that the this.handler function must use bind(this) like so

this.closeHandler = function ( ) { this.removeDialogBox(); }.bind(this);

If you don’t bind the function expression then you will get an error about this.removeDialogBox is not a function.
This is because you are storing the closeHandler in memory for later use.  At that later time the context will be different, the code won’t be executing within your class anymore, it will be in it’s own context. That means “this” that was alive in your class, no longer exists. Which means that function no longer exists You must bind “this” by using “.bind(this) at the end of the function.

And now about the Custom Event. You will see it at the bottom of the removeDialogbox() function

const dialogEvent = new CustomEvent('dialogClosed');
document.body.dispatchEvent(dialogEvent);

Those two lines is all it takes to create and dispatch your own Custom event. This means you write code that listens for the custom “dialogClosed” event to be fired like this.

document.body.addEventListener('dialogClosed', function (){
overlay.hideOverlay();
});

Notice I am using document.body this is a very easy way to create the listener. This is using an anonymous function which is bad because this listener can’t be removed. It should be removed right below this. In order to do that you would need to create the handler function above it and pass it to both the add and remove event listeners.

Adding information to the event.
This is one of the most handy parts of custom events, the ability to pass information in the event. This can be any information, a full object even.
In this article it mentions adding custom data with “detail”.  Here is an example from the code above.

const dialogEvent = new CustomEvent('dialogClosed',{ detail: {
        id: this.dialogBoxId
    } });

Here I am passing id you can use this same format to pass many more values just add a comma to the end of each one. Then to access the extra information in your listener you do like this.

document.body.addEventListener( 'dialogClosed', function (event) {

let dialogId = event.detail.id
switch (dialogId) {
case mainDialogId :
mainOverlay.hideOverlay();
break;
case colorDialogId :
overlay2.hideOverlay();
break;
}

}, false);

Above I am using event.detail.id to get the value I stored in id in the detail of the custom event. Notice how I am using a switch statement to compare the id’s of the dialog that closed to close the correct overlay. There is no default behavior for this action, either one dialog closes or another. I could have 5 different dialogs if I wanted.

Categories
Software Development Web Development

What does prototypical Javascript look like?

Way back before modern times, like 10 years ago. Javascript had a much funkier way of defining objects. It was called prototypical inheritance. This is still how Javascript works, the classes, modules etc. were all recent additions to the language to make it easier to work with. It is not a very fun way to program because it is like looking at a GIANT JSON more than a class with methods.

So what did/does prototype inheritance look like? Well this…



function JsCollection() {
    this.jsObject = new Object();

}
JsCollection.prototype = {
    constructor: JsCollection,
    addNamedProperty: function (property, value) {
        // only add the property if it doesn't exist, return true if it was created
        //return false if it was not, to allow for testing before adding a new property
        var returnBool = false;
        if (!this.jsObject.hasOwnProperty(property)) {
            returnBool = true;
            this.jsObject[property] = value;
        }
        return returnBool;
    },
    getElementCount: function () {
        var elementCount = 0;
        //loop through the object and add to the count
        for (var elem in this.jsObject) {
            //only add to the value if it is part of collection
            //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
            if(this.jsObject.hasOwnProperty(elem)){
                     elementCount++;
            }
           
        }
        return elementCount;
    },
    getElementValue: function (property) {
        var returnProp = null;

        if (this.jsObject.hasOwnProperty(property)) {

            returnProp = this.jsObject[property];
        }
        return returnProp;
    },
    removeNamedProperty: function (property) {

        if (this.jsObject.hasOwnProperty(property)) {
            delete this.jsObject[property];
        }
    },
    changePropertyValue: function (property, value) {

        if (this.jsObject.hasOwnProperty(property)) {
            this.jsObject[property] = value;
        }
    },
    getAllNamedProperties: function () {
        return this.jsObject;
    },
    namedPropertyExists: function (property) {
        var propExists = false;
        if (this.jsObject.hasOwnProperty(property)) {
            propExists = true;
        }
        return propExists;
    }
};

//var objProps = obj1.getAllNamedProperties();
// how to loop through object properties 
//for(var prop in objProps){
//    console.log("Property is " + prop + ' Property value is ' + objProps[prop]);
//}

As you can see this is an object with functions in old fashioned Javascript syntax. This was too confusing of a syntax for most people, plus you had to learn the inner workings of Javascript and how prototypical inheritance works.

I won’t try  to explain it here as it is pretty complicated to wrap your head around. Some videos may help better than an article.



Categories
Resources Web Development

Single page apps suck links and resources

Single page apps built with Javascript are the super hyped rage these days. This design goes against all of the hard learned lessons of the past 30 years.

The main reason SPA’s suck is networks are unreliable and SPA require large amounts of Javascript to be transferred to a users device. Most devices are mobile these days. If a users has a slow connection the app will take for ever to load or timeout and not load at all.

Another reason SPA’s suck is they assume all users are using the latest greatest highest powered device. This leaves out more than half the planet and is a very arrogant approach basically saying you are not important to us because you are poor and have a crappy device and slow connection go elsewhere you peasant.

I’ll post links here as I get time. The first one is an excellent piece covering much of why SPA’s are not a good choice.

Why you should not build your start-up as Single-Page Application?

Categories
Resources Web Development

How to install and update NVM node version manager on Ubuntu

New to node and need to install npm, node.js? Use NVM node version manager so you can install more than one version of nodejs.

I had to look for the answer to this until I found it. You update to a newer version of NVM node version manager the same way you install it regardless of how you install it.  I am not sure what happens if you install nvm with curl then try to update it in another way.

For example I used the following in my terminal as not the root user, just a plain user
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash

 

When I want to install the next version  I replace the v0.35.3 with the next version and it does all the work.  If you installed nvm as a root user then you will not be able to access it easily as any other user.

In fact, if you type nvm –version in the command line as a regular user after you installed it as a root user, you will be told it is not installed, or nvm is not a recognized command would you like to install.   I did this, so that is how I learned not to install it as a root user.

Never ever, ever, not ever use sudo when installing node, npm, nvm or anything like yarn it totally fucks every last thing up.

If you accidentally use sudo follow these instructions to fix the bowl of mashed spaghetti you end up with.

The node world feels as disorganized as Scala here is a link to the latest version

If you are using windows you can install nvm by following these instructions

More info and resources about nvm here.

More info and resources about node package manager

Categories
Resources Software Development Web Development

node version manager nvm resources

Articles

Getting started with nodejs, nvm, npm

Videos

How to Install Node Version Manager on Ubuntu

Categories
Resources Web Development

Webpack resources, links and videos

Frontend development has become a challenge. These days we need all kinds of tools. Instead of having one large Javascript file or one large CSS file, you need to develop modularly and therefore you need to break up your files into many smaller files. This leads to all kinds of issues, especially with javascript since the order that your javascript is loaded can mean errors or not. “path”: __dirname+’/static’,
“filename”: “[name].[chunkhash:8].js”

javascript pro tip
javascript pro tip meme

I have used many tricks in the past such as creating my own BASH scripts which collected the files from folders and checked the order and trans-piled and minified them to single files. Even that process got to be a pain because it is hard to make sure things are loa “path”: __dirname+’/static’,
“filename”: “[name].[chunkhash:8].js”ded in the right order.

Webpack makes it easier

Instead of explaining webpack in a lot of paragraphs, I will create a list of videos and articles  I find helpful. First off what is Webpack? I found this video to be very helpful.
“path”: __dirname+’/static’,
“filename”: “[name].[chunkhash:8].js”

 


Customize Bootstrap 4 with Webpack | Webpack tutorials


Webpack 4 Tutorial – Getting Started for Beginners


Learn Webpack – Full Tutorial for Beginners


 

Resources, Articles & links

A Beginner’s Guide to Webpack 4 – A great article to get started with.

An introduction to source maps – An article about using source maps in javascript

Webpack using Source Maps – Webpack documentation page for source maps

Webpack source map devtool – Webpack documentation for the devtool that creates source maps, this lists the options

Webpack SourceMapDevToolPlugin – documentation, this plugin aids the source map devtool to add more features and options.

A mostly complete guide to webpack (2020) – a great article about webpack

Creating a custom webpack plugin – decent article about the basics of creating a webpack plugin

Categories
Resources Web Development

React information and resources

Why you shouldn’t use inline styling in production React apps

Why you should use refs sparingly in production

Do React Hooks Replace Redux?

Videos

Learn REACT JS in just 5 MINUTES (2020)


JavaScript for React Developers | Mosh

Categories
Resources Web Development

Redux information and resources

Why use redux, reasons with clear examples

Logrocket it helps you find bugs in your redux apps

What Is Redux: A Designer’s Guide

Do you need redux with react hooks?


Videos

What is Redux?

Categories
Resources Web Development

Javascript resources

javascriptweekly – keep up to date on the latest javascript news

How to deal with dirty side effects in your pure functional JavaScript

async/await is just the do-notation of the Promise monad

Do we really need classes in JavaScript after all?

The cost of javascript. Javascript is often misused and abused or poor practices are used. This article covers some of the things you can do to lighten your apps javascript load and clean up your code a little.

Should You Use Classes in JavaScript?

A re-introduction to JavaScript (JS tutorial)  – because developers couldn’t be bothered with the hard task of learning actual Javascript syntax we had to pollute the syntax even more with Classes etc.

Function expressions explained

Arrow function expressions explained – examples and explanation

Mocking is a code smell – interesting article about how you can get https://krasimirtsonev.com/blog/article/javascript-managing-events-dispatch-listenout of control with testing and dependency injection. It is about javascript but applies to other languages.

Eradicating memory leaks in Javascript – A very good article for anyone to who creates Apps with Javascript.

Javascript managing events

Javascript let vs var vs const – a good article explaining the differences.

Javascript free books and resources – nice article with links to various sources.

javascript.info – a really awesome site that teaches everything you need to know about modern Javascript.