Stuff I'm Up To

Technical Ramblings

Opening another Window with electron-vue — October 9, 2018

Opening another Window with electron-vue

Having entered the world of Electron and mashing it together with Vue.js using electron-vue I needed to figure out how to open another window from Electron and still have vue active within it and better yet, still have the hot module reloading active in that new window whilst developing it.

It was whilst trawling the electron-vue github issue page that I came across a golden nugget of code that answered the very question for me.

https://github.com/SimulatedGREG/electron-vue/issues/401#issuecomment-330656658

Quoted:

First of all, you need to disable mode: 'history' in your vue-router, check in vue-router docs

Then do the following:

src/main/index.js example

ipc.on('showChart', function (e, data) {
  const modalPath = process.env.NODE_ENV === 'development'
    ? 'http://localhost:9080/#/showChart'
    : `file://${__dirname}/index.html#showChart`
  let win = new BrowserWindow({ width: 400, height: 320, webPreferences: {webSecurity: false} })
  win.on('close', function () { win = null })
  win.loadURL(modalPath)
})

In your router, use the exactly path to your url

src/renderer/router.js example

{
  path: '/showChart',
  name: 'showChart',
  component: require('your-router'),
},

 

Advertisements
When this isn’t this and becomes that — October 2, 2018

When this isn’t this and becomes that

I recently tried to assist a colleague with an issue in JavaScript involving an undefined variable within a Vue.js app.

Now I have encountered this issue several times and never really gotten to the crux of the matter other than I know it’s because the context of this changes, depending on where you are in your code.

It was during this tongue twisting exercise that he found a useful document that gives an outline more elegantly than I could phrase.

https://gist.github.com/JacobBennett/7b32b4914311c0ac0f28a1fdc411b9a7

Since arrow functions provide a lexical this value, the this inside our function() refers to the window instead of our Vue object which breaks our current implementation! When attempting to get this.item, we will actually be looking at window.item which is currently undefined.

electron-vue —
React and Webpack – Project Template — September 22, 2018

React and Webpack – Project Template

I’ve begun looking at building a project using React and followed some online sources to begin with. But then I fell into outdated material that related to babel. So I could only take many of the tutorials so far before having to update the construction to suit @babel/core v7.

It seems it’s very easy to install different versions of babel and it’s components and then discover things won’t compile. The error message I was getting:

Error: Plugin/Preset files are not allowed to export objects, only functions.

It doesn’t exactly make clear that what you have probably done is installed a preset  from an older version of babel and your babel core doesn’t like it.

Continue reading

Laravel API and Bootstrap Form Validation — August 13, 2018

Laravel API and Bootstrap Form Validation

This caused me some grief today. I spent the day adding validation rules into my Laravel resource controller and rather foolishly set HTML5 validation parameters on my Vue.js / Bootstrap 4 form component.

Why foolishly?

Well if you follow the Bootstrap 4 JavaScript function to call form.checkValidity() you’re actually calling the HTML5 built in function. Not a Bootstrap function as I originally thought.

When Laravel validation failed at the resource controller and it pushes back 422 (Unprocessable Entity) and a json error object:

{"message":"The given data was invalid.","errors":{"name":["The name field is required."]}}

I thought Bootstrap was seeing the Laravel validation errors and flagging up fields as not valid. So I could not understand why one of my fields didn’t show as invalid when according to Laravel it was!

What I was actually doing was HTML5 validation and ignoring my Laravel validation response all together. With the API it’s best NOT to try to use both HTML5 and Laravel validation. You’ll get a confusing UX that uses a mix of browser error messages/popups and Bootstrap CSS error handling.

Make sure you add novalidate to your form tag – this ensures HTML5 browser validation is prevented at the form level.

To resolve the Laravel validation part I just use the Laravel json error object and DON’T USE checkValidity(), my axios .catch(error) processes the Laravel errors by calling showErrors(error.response.data)

 this.axios.post('/api/v1/mycall/',
   this.data
 ).then(() => {
   // That worked out well, do something.
 }).catch(error => {
   this.showErrors(error.response.data)
 })

 showErrors: function (error) {
  Object.keys(error.errors).forEach((field) => {
    let input = document.getElementById(field)
    input.classList.add('is-invalid') // Bootstrap invalid form input
  })
 }

This iterates through the json errors and adds the class is-invalid to the fields that Laravel tells me are invalid. This triggers Bootstraps CSS to show the field with a red border and unhides the form-control subsequent/child div that has a class of invalid-feedback

References

https://getbootstrap.com/docs/4.1/components/forms/#validation

When using Vue.js and Laravels json response the actual usage is closer to the server-side examples:

https://getbootstrap.com/docs/4.1/components/forms/#server-side

Linting — August 6, 2018

Linting

No, this isn’t about taking the fluff from your belly button – but you’re close.

https://eslint.org/

Linting your written code is a method of ensuring that it meets consistent syntax and style guidelines. Eg. ensuring you indent function blocks by 4 spaces and not tabs, placing curly braces {} on new lines, having spaces following function names and parameters etc.

JavaScript, being a dynamic and loosely-typed language, is especially prone to developer error. Without the benefit of a compilation process, JavaScript code is typically executed in order to find syntax or other errors. Linting tools like ESLint allow developers to discover problems with their JavaScript code without executing it.

ESLint is a great way of ensuring my (ECMA)JavaScript coding style is consistent and correct. Using it within atom.io is a great way to keep my work tidy. Just add the ESLint package and we’re set to track and tidy my .js files.

But I want the same rules for my .vue files. I found this really handy: https://alligator.io/vuejs/vue-eslint-plugin/

Add the atom package linter-eslint then go to its settings button.

Find the option “List of scopes to run ESLint on…” and add onto it:

text.html.vue

So in my case it becomes:

source.js, source.jsx, source.js.jsx, source.babel, source.js-semantic, text.html.vue

 

I Hate Internet Explorer — August 1, 2018

I Hate Internet Explorer

I just can’t seem to get it to die! Probably something to do with all those users out there spoiling my day and continuing to use it in Windows 7 – sadly in our corporate environment.

I’ve spent a few days with a problem showing “Syntax Error” in IE11 when my project runs just great in Edge, FireFox and Chrome.

After a lot of digging around in the components I’ve used I narrowed it down to one specific 3rd party component from npmjs. So I took a trawl through into the Github of the project and opened an issue. Turns out I’m not the only one seeing the problem. I guess everyone else is using more sensible browsers.

I got a great reply that pointed me to modifying my webpack config so it transpiled into IE11 compatible code. I’d already used polyfills like es6-promise and es6-object-assign and then babel-polyfill, but this obviously wasn’t enough.

The pointer was aimed at webpack. Now Laravel uses it’s own layer above webpack – Laravel-Mix, but with a bit more Googling I figured out how to add the necessary webpack config into mix to get it transpiling correctly.

Add the targets-webpack-plugin using yarn (or npm) as a “devDependency”

$ yarn add targets-webpack-plugin -D

In my webpack.mix.js file:

const TargetsPlugin = require('targets-webpack-plugin')

...

mix.webpackConfig({
  plugins: [
    new TargetsPlugin({
      browsers: ['last 2 versions', 'chrome >= 41', 'IE 11'],
    }),
  ]
})

Repackage my app with yarn run dev and IE11 is silent!

The internet is full of wonderfully helpful people. To those that post responses, write blogs or any kind of feed – I thank you all.

 

Yarn, npm and Git — July 31, 2018

Yarn, npm and Git

I’m kinda new to this hosting software externally. I’ve been happy using Gitlab for internal private projects, but recently I’ve been forking and reworking the work of others to include into my own.

So I thought I’d try publishing them back out to npmjs.com and github,com.

Publishing up to github is pretty straight forward. Using git from the command line to just add, commit and push is all the same, just now with an online repository rather than the internal Gitlab. In fact once the repository is added to the project I’m betting the Atom.io built in Git will handle the committing and pushing.

Recently I decided to have a look at yarn – it uses the npmjs repository just like npm. So far I’m liking it. It’s kinda pretty and I do like the caching.

I pretty much use yarn where ever I would have used npm. So instead of npm init I use yarn initnpm install I now use yarn add. Where I used npm run dev now it’s yarn run dev, and yarn run hot, etc.

Publishing

Now to push my packages up to npmjs. First you have to have an npmjs account, so sign up. Like Github, npmjs is only free for “public” packages. If you want “private”, you have to pay.

Then link yarn and npm up to your npmjs account using login.

$ yarn login
yarn login v1.7.0
question npm username: mynpmjsname
question npm email: me@mail.com 
Done in 32.16s.

Similarly for npm.

This saves an auth token into your .npmrc file so if you’ve supplied the password already you won’t need it to publish with. What I found after pushing up to git and publishing to npmjs was that I was doing things in the wrong order. So used to doing git commit and push, it’s second nature.

Before you git push to github, publish the package to npmjs. Do it this way because it will force a version increment which will do a git commit. Because it will have incremented the version in your package.json.

The name, version and description etc. in your package.json is what will be used by your project.

Because you are likely to want to publish what is probably a private project name (beginning with @) to a public project you’ll need to tell your publish command to make it public, or you’ll get a warning pretty much about about not having subscribed to publish private projects.

$ yarn publish --access=public

When you look at your projects in your npmjs.com profile you’ll see your fresh project ready for everyone to consume!

$ yarn publish
yarn publish v1.7.0
[1/4] Bumping version... 
info Current version: 1.8.1 
question New version: 1.8.2 
info New version: 1.8.2 
[2/4] Logging in... 
[3/4] Publishing... 
success Published. 
[4/4] Revoking token... 
info Not revoking login token, specified via config file. 
Done in 8.85s.

But that’s how easy it was to publish up to npmjs!

Now you’ve pushed to npmjs, you can do a git push to deliver the same project up to Github.com now with the new version.

References

https://yarnpkg.com/en/docs/creating-a-package

Vuetable-2 — July 26, 2018

Vuetable-2

Previously I’ve used Datatables.net to build my tables on the Laravel blade templates for user interface. Now I’ve been migrating over to Vue.js I thought I’d look at another option – more Vue.js centric.

This is where vuetable-2 (not to be confused with other vue components of a similar name!) came in. I tried a lot of other vue tables and most of them had a dependency for jQuery. I really wanted one that didn’t use jQuery – I know Bootstrap has it as a dependency, but if I ever decide to remodel the UX using something like Material Design, I’d have no need to include jQuery.

As a component I needed to do a lot of work to configure it to work with my setup. But only really because I wanted it to work with Bootstrap 4 and fontawesome. So I had to build vue component templates that presented me with the correct structure and classes for the pagination and search.

The examples in the Gists relate to my admin interface to manage a Laravel user model of users and roles.

(see gists below)

Continue reading

Laravel and Vue.js Authentication — July 25, 2018
Laravel 5.5 and Hot Module Reload — July 20, 2018

Laravel 5.5 and Hot Module Reload

Revisiting a previous post  about vue-cli 3 and hmr I tried to get HMR going in a similar fashion through Laravel-mix.

First mistake to make is that laravel-mix does not need BrowserSync for HMR. So don’t install it or configure it in the webpack.mix.js file.

HMR on Laravel 5.5 is loaded by running the package.json “hot” script:

$ npm run hot

It compiles the assets and sits there doing apparently nothing. When actually it’s listening on localhost:8080 for HMR/WDS connections. Whilst in this state if you open another session and serve your project using artisan, HMR just works…

$ php artisan serve

… if you are developing on the same “localhost” as the HMR and artisan server are running on.

But what if you’re not?

I tend to fire up a virtual host with Laravel installed so can’t access it as “localhost” I must use one of it’s public interfaces such as 192.168.56.2.

To make laravel-mix HMR run on one of your public interfaces you’ll need to edit you webpack.mix.js file and add it the following as per your serving host:

mix.options({
  hmrOptions: {
    host: '192.168.56.2',
    port: 8080
  }
});

In your blade template ensure you refer to your assets using the form src="{{ mix('js/app.js') }}" as using it like this handles adjusting the host in the blade based on if you are running the hot script or not.

You MUST run two sessions to use HMR. One to run the hot compiler and one to serve the php environment with artisan. I’ve had frustrating times trying to use & for task spawning.

In session one:

$ npm run hot

In session two:

$ php artisan serve --host 192.168.56.2

Visit your app at: http://192.168.56.2:8000 and you’ll get your artisan served php project.

If you inspect the page in your browser you’ll see the mix src becomes //192.168.56.2:8080/app.js because of the webpack.mix.js change – NOT the artisan serve.

A Further Note

The ability to overwrite the config for hmtOption looks like a laravel-mix ^2.0 thing. I just spent an hour wondering why another project was failing to run on anything other than localhost when the option set.

Upgrading to laravel-mix ^2.0 in composer.json​ resolved the problem.

 

vue-cli 3 and hmr — July 18, 2018

vue-cli 3 and hmr

What a nightmare I’ve had. Trying to figure out how to run a dev server with HMR (Hot Module Reload) on a virtual host that has two interfaces – one NAT and one Host Only adapter!

Why the two interfaces? Well I have to NAT one out from the guest OS so traffic looks like it comes from my business PC that uses network authentication. But NAT means I have no access back to the dev server as it uses an ip of 10.0.2.15 (typical virtual box behaviour).

So to gain access to the dev server from my host I then use another NIC in the virtual guest that uses the “Host Only Adapter”. So this ends up using a static IP address of 192.168.56.2 – again typical virtual box behaviour.

So I can access the virtual guest as http://192.168.56.2:8080 to connect to the webpack dev server, but then I see errors in the client browser console:

+ [HMR] Waiting for update signal from WDS
+ [WDS] Disconnected

When I expanded the [WDS] Disconnected message I could see the clue being:

./node_modules/webpack-dev-server/client/index.js?http://10.0.2.15:8080/sockjs-node

It’s obviously telling my client to use the wrong network interface for the websockets/sockjs connection used by HMR.

How to fix it was a mystery. I had to trawl until I found the @vue\cli-service\lib\serve.js file. In here it starts the sockjs with options like this:

const publicOpt = projectDevServerOptions.public
const sockjsUrl = publicOpt ? `//${publicOpt}/sockjs-node` : url.format({
  protocol,
  port,
  hostname: urls.lanUrlForConfig || 'localhost',
  pathname: '/sockjs-node'
})

This meant I could look to the vue-config.js​ file in the root of my project ad set the ‘public’ variable accordingly. I also set the proxy option to handle the fact I’m not developing on the same server.

// vue.config.js
module.exports = {
  devServer: {
    useLocalIp: false,
    proxy: 'http://localhost:8080',
    public: '192.168.56.2:8080',
  }
}

Then I started the dev server using yarn, I could also use npm I guess.

$ yarn serve
...
DONE Compiled successfully in 1002ms 21:39:53

  App running at: 
  - Local: http://localhost:8080/ 
  - Network: http://10.0.2.15:8080/

Now when I edit code and webpack recompiles, the HMR works and the browser updates!