VueJS static site search and Pelican

I’m slightly chuffed by this, I was doing something and build Vue.js based search from a statically created index.

The fun part was storing/passing a “category” variable from the page’s HTML to the Vue app, and only slightly problematic by using Pelican to generate the site.

Pelican uses {{}} for tempalte variables, same as Vue, so I had to reconfigure Vue’s delimiters (hence the || around item.message)

Passing the category from the static HTML to Vue

  • The searchmeta app is defined in the div#searchmeta tag.
  • The data-category attribute of the div#searchmeta tag is generated by Pelican. It’s used on the categories pages, so that search only shows the in-category ports.
  • searchapp.getCategory() looks for the attribute and sets it to searchapp.category

Generating the search data

I generate a list of the pages, like [ "tcp/1", "udp/53" ] etc, and it’s stored as a static JSON file in the Pelican theme. In this case as /theme/searchdata.json.

I’m sure I could do this with Pelican automagically, but I was already pretty done with yak-shaving at that point. I was converting portDB from Jekyll to Pelican, because I … had to fix some other thing that didn’t need fixing. It’s yaks all the way down.

Searching

  • searchapp.portFiltered(), which takes the search string from the input#searchTerm element.
  • As that’s updated, it re-generates the the <template v-for tag and its contents.
 <div class="wrapper main" id="searchmeta" name="searchmeta" data-category="{{category}}">
    <input type="text" id="searchTerm" name="searchTerm" placeholder="Port # Search" v-model="portFilter"/>

    <ol>
    <template v-for="item in portFiltered.slice(0, 20)">
    <li style="list-style-type: none;">
        <a :href="'/'+item.message+'/'">| item.message |</a></li>
    </template>
    </ol>
</div>
var searchapp = new Vue({
    delimiters: ['|', '|'],
    el: '#searchmeta',
    data: {
        ports: [
            { message: "loading"},
        ],
        portFilter: '',
        category: '',
    },
    created () {
        this.updateData();
    },
    computed: {
        portFiltered() {
          if (  this.getCategory() != "" && this.portFilter != "" ) {
                return this.ports.filter(port => {
                    const portToMatch = port.message.toLowerCase();

                    return portToMatch.includes(this.getCategory()) && portToMatch.includes(this.portFilter);
                }).reverse();

            }

            return [];
        },
    },
    methods: {
        getCategory: function() {
            const result = this.$el.getAttribute("data-category");
            if (result != undefined) {
                return result;
            }
            return "";
        },
        updateData: function() {
            axios
            .get("/theme/searchdata.json", headers={'crossDomain': true})
            .then(res => {
                let data = res.data.map(function(el) {
                    return { "message" : el.toLowerCase() };
                });
                this.ports = data.reverse();
            });
        }
    }
})


#portdb #yak shaving #vue.js #python #javascript