Friday, July 6, 2018

Handling new POI's publicPath

Today I wasted over 3 hours frantically looking for explanation why the hell POI's publicPath setting thinks I am an idiot and it knows better what I want. So this is a quick post to let you know what you should do if you really want to change the output.publicPath in a POI-managed project.

The problem

Imagine you're working on some project that has pre-existing sources and their location is (surprisingly) not in the root of your server. That's the case every single time when you're working on existing apps trying to induce some build system on them to bundle up the scripts and stylesheets.

Usually, for those cases Webpack has a special configuration option output.publicPath that handles everything for you and life's good. POI, on the other hand, has that option exposed at the top level - but with a twist: it only works in final build and not in development mode.

The problem is, however, that if you'd like to work against an existing server that produces HTML and other artifacts you need to comply with that structure regardless of the mode. Unfortunately, POI fucking knows better! And here's the relevant point in POI's code that does it:


function getPublicPath(command, publicPath) {
  if (command === 'build' && typeof publicPath === 'string') {
    return /\/$/.test(publicPath) || publicPath === ''
      ? publicPath
      : publicPath + '/'
  }
  return '/'
}

module.exports = getPublicPath

I have no idea what was going through EGOIST's head at the time of writing but it must have been something really, really strong. Not good for opensource as it breaks the fundamental rule of least surrprise.

The fix

Instead of setting the publicPath in poi.config.js or setting it via command-line parameter override it in the resulting Webpack configuration like so:

module.exports = {
  configureWebpack(config, context) {
    // we're not setting publicPath as the general configuration option
    // because some sick bastard decided this will only have an effect
    // in production mode and we need it everywhere. Talk about predictability...
    config.output.publicPath = '/path/where/everything/lives/'
    return config
  },

With that life's good again (even thought it was so damn frustrating Today)

The architecture

Working on projects where the content is delivered from some sort of CMS or other app that has a fat backend isn't easy. It usually boils down to having everything running locally, even if we don't need it. There is, however, a better way of doing it! Use some reverse proxy to route traffic to your browser from 2 different sources: the backend and the frontend in development mode. Here's an example configuration with Apache2 but you can do much much more than that:

<VirtualHost :80>
    ServerName my-app.local

    # Make sure no caching is induced so that refresh always brings the latest version
    # run "sudo a2enmod headers" to enable headers module
    Header set Cache-Control no-cache

    # run "sudo a2enmod proxy_http" to enable http proxy
    ProxyRequests     off
    ProxyPreserveHost off

    # Serve all theme files from local development server
    ProxyPassMatch    "^\/path\/where\/everything\/lives\/(.*)$" "http://localhost:10001/path/where/everything/lives/$1"

    # Required by Hot Reload (run "sudo a2enmod proxy_wstunnel" to enable ws proxy)
    ProxyPassMatch    "^/(.+).hot-update.js$" "http://localhost:10001/$1.hot-update.js"
    ProxyPassMatch    "^/(.+).hot-update.json$" "http://localhost:10001/$1.hot-update.json"
    ProxyPassMatch    "^/sockjs-node/(.+)/websocket" "ws://localhost:10001/sockjs-node/$1/websocket"
    ProxyPassMatch    "^/sockjs-node/(.+)$" "http://localhost:10001/sockjs-node/$1"

    # Serve everything else from remote server
    ProxyPass         / http://dev-machine.somedomain.com/
    ProxyPassReverse  / http://dev-machine.somedomain.com/

</VirtualHost :80>

Also, don't forget to set NameVitualHost *:80 or the name-based virtual hosting just won't work. Now with that in place add the following entry to your /etc/hosts file:

127.0.0.1   my-app.local

and you're all set.

You can of course fiddle with it, add SSL support if your backend server requires it, but the point here is that it is possible and you can work without installing heavy backend to work on frontend!

Happy coding!

Saturday, June 23, 2018

Scaffolding a new Vue.js component

I've been working for the past few months on a travel portal rewriting parts of it from old jQuery code to Vue.js - and learning a lot of stuff in the process. One thing that I learned the hard way is that scaffolding that's built in into Vetur is just not for me. It is too simple.

The naming convention we have for our component files is for them to have the same name as the file they exist in. So basically TestMe.vue becomes

<template>
  <div class="test-me">
    ...content
  </div>
</template>

<script>
import Vue from 'vue'
import Component from 'component'

@Component({})
export default class TestMe extends Vue {
}
</script>

<style lang="scss" scoped>
.test-me {
}
</style>

Makes sense?

The problem is that having to type this in every time I create a new component (and the number of those grows like creazy!) is tedious. So I finally got to it and created a snipped that works for me:

That's it! You type "sfc", press [Tab] and the component is ready to be worked on by magic of the VS Code templating engine.

The first thing that'll be edited is the name of the exported class. If it is OK then just press [Tab] to move to the name of the CSS class. Edit it if you must then press [Tab] again and you'll be able to select the processor for styles. The next thing that you get to customize is if the styles should be scoped. Once on it either press [Tab] to go with scoped styles or [Delete] and then [Tab] to go without scoped styles. Finally you arrive at the Hello, world! text where the editing of your component begins.

Have a nice day!

Saturday, June 16, 2018

Vue.js - editor components

When talking about creating applications in Vue.js it is not hard to find one that does something to data. Obviously for state management there's Vuex, Redux and other stores but in this post we're going to focus strictly on passing state via props to components that one could generally call controls - so more on the lines of custom inputs.

The 3 cases

There are 3 cases that we will encounter when passing data down to controls:

  • We're passing in primitive values (strings, numbers, booleans)
  • We're passing in complex reactive objects (think: list of people)
  • We're passing in a complex reactive object but we'd like to treat it as a primitive and have atomic changes to all its fields

Primitives

In the case of primitives the situation is dead simple: use v-model, you can react to changes by hooking up to the input event - done. The documentation is fantastic in that area so if you'd like to know more about it dig in!

Reactive complex objects

Imagine you have a data structure like this:

data = { firstName: 'John', lastName: 'Doe' }

In this case if you create a control (we'll call it DataEdit.vue) that you'd like to immediately edit the fields you could do something like that:

<template>
  <div>
    <input v-model="value.firstName">
    <input v-model="value.lastName">
  </div>
</template>

<script>
export default {
  props: {
    value: Object
  }
}
</script>

This means that if used in a parent component (ContactForm.vue) like so:

<template>
  <div>
    <h1>{{ person.firstName }} - {{ person.lastName }}</h1>
    <DataEdit :value="person" />
  </div>
</template>

<script>
import DataEdit from './DataEdit.vue'

export default {
  components: {
    DataEdit
  },
  data() {
    return {
      person: { firstName: 'John', lastName: 'Doe' }
    }
  }
}
</script>

then changes to values in inputs in DataEdit.vue component will immediately be reflected in ContactForm.vue. Vue's reactive system at its best.

Treating complex objects like a value

This is the trickiest one because even though we're passing on a complex, reactive object we'd like to get all the changes at once or none at all. You might ask why would you want such a thing? The answer is quite simple: you'd like to implement "OK/Cancel" functionality or (if the edits drive some kind of Ajax requests) limit the number of actions upon edits. It is quite obvious that there will be a need for a copy of the reactive object. For that I use cloneDeep method from Lodash and it works just great so far.

<template>
  <div>
    <input v-model="internal.firstName">
    <input v-model="internal.lastName">
    <button @click="$emit('input', internal)>Save</button>
  </div>
</template>

<script>
import cloneDeep from 'lodash/cloneDeep'

export default {
  props: {
    value: Object
  },
  data () {
    return {
      internal: cloneDeep(this.value)
    }
  }
}
</script>

Now if that component is used in the ContactForm.vue (note the change from :value to v-model)

<template>
  <div>
    <h1>{{ person.firstName }} - {{ person.lastName }}</h1>
    <DataEdit v-model="person" @input="personUpdated" />
  </div>
</template>

<script>
import DataEdit from './DataEdit.vue'

export default {
  components: {
    DataEdit
  },
  data() {
    return {
      person: { firstName: 'John', lastName: 'Doe' }
    }
  },
  methods: {
    personUpdated(newValue) {
      console.log('Person has been updated to: ', newValue)
    }
  }
}
</script>

you won't see any changes to the header until they are saved by clicking the Save button. Pretty neat, right? On top of that you can be notified when the change occurred so if some additional action needs to take place (like updating list of people from an external database) by listening to the input event. That is just pure awesome!

One more thing...

If the editor persists it will now share the internal and value objects which will make it behave like the case where everything is reactive. Not good - let's do something about it

<template>
  <div>
    <input v-model="internal.firstName">
    <input v-model="internal.lastName">
    <button @click="$emit('input', internal)">Save</button>
  </div>
</template>

<script>
import cloneDeep from 'lodash/cloneDeep'

export default {
  props: {
    value: Object
  },
  watch: {
    value: {
      handler (newValue) {
        this.internal = cloneDeep(newValue)
      },
      deep: true
    },
  },
  data () {
    return {
      internal: cloneDeep(this.value)
    }
  }
}
</script>

The introduced watch updates the internal state so that it is again disconnected from the ContactForm.vue. Of course in a situation where the DataEdit.vue component is removed from DOM due to let's say closing a popup then the watch is completely unnecessary. It does however come in handy if there might be a possibility that the data object in question (or some of its parts) can be modified from the parent component. The internal state will be out of sync in such case. This might happen if some of the details come from an Ajax request or a timer. The watch covers both cases so it is basically a universal way for data synchronization on changes from parent component.

Working example

I know this is a lot to take in at once. Therefore I have prepared a test application for you that illustrates all the pieces. You can find it at https://github.com/padcom/vue-editor-components

That's it, folks!

Monday, April 9, 2018

Vue.js and functional single file components

Single file components (SFC) is probably the most powerful feature (structure-wise) in Vue.js. They are simple in nature but very powerful when it comes to bundling things together:

<template>
  <h1 class="my-header>Hello!</h1>
</template>

<script>
export default {
}
</script>

<style>
.my-header {
  color: red;
}
</style>

There are times when you want your component to be stateless. Such components are great for bundling together functionality that would otherwise be created by putting together other components. I call them template components, because they are predefined with places where you can override the defaults. Those components do not have state hence we call them stateless or functional.

The problem with SFCs is that unlike regular .vue components they don't pass data down. Especially the class and style properties are not passed automatically. To mitigate that I went through a few iterations to find the nicest solution. Here's what I came up with that seems to do the job very well. Let's assume we're creating a predefined component for animation. We want the styles to be bundled with the component so SFC is the right way to go.

<script>
export default {
  functional: true,
  render: (h, { data, children }) => (
    <transition-group { ...data } tag="ul" name="slide">
      { children }
    </transition-group>
  )
}
</script>

<style>
.slide-move {
  transition: transform 1s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
</style>

A quick explanation: { data, children } is a destructing assignment from the second parameter to 2 fields that we'll use later. Then the { ...data } means "apply all elements passed on (including but not limited to class and style).

That's all there is to it.

Thursday, March 22, 2018

HttpHandler registration on XSP4 vs IIS

The problem

You're developing an ASP.NET HttpHandler on Mono using XSP and now you need to deploy your app on IIS. What worked on XSP4 isn't working anymore and strange errors appear.

Let's start with the config that works on XSP4

<?xml version="1.0"?>
<configuration>
  <system.web>
    <httpHandlers>
      <add verb="*" path="*" type="MyHandler" />
    </httpHandlers>
  </system.web>
</configuration>

But you put that same Web.config file on IIS it throws errors.

The solution

As you see the registration happens inside system.web/httpHandlers section. That works on XSP4 and older ASP.NET but it's not the case on modern IIS. In there you need to switch to system.webServer/handlers section like this:

<?xml version="1.0"?>

<configuration>
  <system.webServer>
    <handlers>
      <add name="name" verb="*" path="*" type="MyHandler" />
    </handlers>
  </system.webServer>
</configuration>

That's it! Happy coding!

Wednesday, March 21, 2018

Getting started with POI and React

This will be a quick one and, quite frankly, a very obvious one. But since I forgot how it is done I better write it down for posterity (and me).

Setting things up

As always you'll need a folder. Let's call it my-example

$ mkdir my-example
$ cd my-example

Then we need to initialize our project:

$ npm init

and accept all the defaults.

Next we need a few packages installed:

$ npm install poi react react-dom --save-dev

Creating poi.config.js

Since by default POI compiles JSX down to Vue's interpretation of what JSX is we need to tell it to use React's JSX compilation. We do that by adding the poi.config.js file with the following content:

module.exports = {
  jsx: 'react'
}

Creating start script

Finally, to make our project setup complete we should add a start script to package.json to make use of the locally installed POI:

"scripts": {
  "start": "poi"
}

Time for a simple app

With all that setup all there is to it is to test it with some React components. For that we will create index.js with the following content to display a Hello, world!-type app:

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(<h1>Hello, world!</h1>, document.getElementbyId('app'))

And just like that, in under 3 minutes, by the magic of POI, we have a fully-working application that can be developed, tested and built for production deployment. I like it!

Happy coding!

Big Fat Ass - Windows + Visual Studio

Warning: this post contains bad language, obscene descriptions and a lot of frustration. Don't read it if you're sensitive.

The fucking nightmare

In the past I used to work with Windows. I liked it - especially Windows 2000 and then XP. It was kind of fresh, you know? Still looking the same as the old win but kinda... better. And after a few small upgrades to the hardware, like a tiny bit more RAM, it worked really fast. I was reluctant to look for other OSes for more reasons than just I got used to it: Delphi, the IDE I was working most of the time in, was not available on other platforms. And I loved Delphi. It was the best thing ever! Especially the Personal edition which was bloody fast and easy to use.

At one point I gave Linux a try. Slackaware to be precise. I felt this was not just for me but also not for any sane human being. I mean come on: compile kernel just to be able to install the distro? Are you fucking kidding me? Then I gave RedHat a try. Well.. better but still: 24 floppies just to install the basic OS? Are you fucking kidding me?!!! And then Ubuntu came along a few years later. I ordered the CD (good old days :D), installed it and liked it instantly. It looked different, foreign... but nice. I got along with the keyboard quite fast, cmd was finally bash and way more humane to work with... Basically, for the first time I didn't immediately went with "You have to be shitting me, man! What a shit hole!". But I was still a Delphi developer so it was more for entertainment than anything else...

Fast forward a few years and that stopped being a problem since I moved to Java.

2018

It's 2018. I work exclusively in Linux, I love every bit of it. I like that I can choose not to butcher my computer with CPU/GPU-intensive operations just to show the content of a fucking folder. I'm loving XFCE and everything about it. I use Linux Mint and my main occupation is frontend development so Windows is pretty much useless anyways. I play on Linux, I watch movies on Linux, I work on Linux. And everyone in my family does too and they like it! My distro of choice is Linux Mint because it is the closest approximation of Windows XP I can get out of the box. And it is good!

And then I decided to give .NET a spin... Man,.. there are decisions that one regrets moments after they are made. At first it wasn't so bad. I still used Linux with Mono, tried Core but it wasn't the right target I was looking for so Mono was the option. Everything seemed to work just fine, MonoDevelop rocks! Fast, easy, nice... I like. I even completed the entire project, was able to test it using XSP...

All was good just up to the point where I needed to test it in real conditions. NOTHING FUCKING WORKED! NOT A SINGLE FUCKING THING! JAVA KILLER MY ASS!!!

So basically creating a solution in MonoDevelop does not mean you will be able to load it into the latest Visual Studio. Windows, when I turned it on, immediately started taking over my CPU fan, spinning it so fucking fast the whole machine became more than just a bit warm. It was FUCKING HOT!!! And still... nothing fucking worked!!!

After a trial and error, looking at Stack Overflow and other enlightened sites I finally was able to throw away all the project files, recreated them in VS 2017 and then it still didn't fucking work! All because some idiot decided to switch from one XML configuration section to another. I mean for the love of God, really?! WHAT THE FUCK!?!?

It took me a better part of this evening to just get it started, then about 3 minutes to add a portion to my node module that converts from fucking backslashes to forward slashes (as though the rest of the fucking world were morons and used the wrong character for path separator) and I was finally on my marry to start hating Windows on my blog.

Post mortem

Unfortunately, as a front developer I need to have access to Edge which, surprise surprise!, is only available on fucking Windows. And this is the only reason I have a separate, dual boot notebook that runs a legal (my God, what a fucking waste of fucking hard earned money) copy of Windows 10.

I need a fucking drink after all this. No wonder .NET developers are so strange... there is a damn good reason for it!

God save us from Windows and .NET. Please!!!