Advanced Components

Warning

Work in progress. The example works but requires much more elucidation

Introduction

In most cases, components can be defined using Python only as previously described in this tutorial. In some cases however, in parallel to the Python definition, a Vue.js component also needs to be created.

In this chapter, we will create a signing pad component based on signature_pad

Info

If you are not familiar with Vue.js, this chapter will not be very useful to you.

The Components Directory

Under your applications static directory (the default is the directory the application is run form) create another directory called components. Put the your JavaScript component definition there. Files must all have the extension .js.

The application will load these files automatically.

Python

import justpy as jp

class SignaturePad(jp.JustpyBaseComponent):

    vue_type = 'signaturepad'

    def __init__(self, **kwargs):

        self.options = jp.Dict()
        self.classes = ''
        self.style = ''
        self.width = 400
        self.height = 200
        self.clear = False
        self.show = True
        self.event_propagation = True
        self.pages = {}
        kwargs['temp'] = False  # Force an id to be assigned to pad
        super().__init__(**kwargs)
        self.allowed_events = ['onEnd', 'onBegin']
        if type(self.options) != jp.Dict:
            self.options = jp.Dict(self.options)
        self.initialize(**kwargs)


    def add_to_page(self, wp: jp.WebPage):
        wp.add_component(self)

    def react(self, data):
        pass


    def convert_object_to_dict(self):
        d = {}
        d['vue_type'] = self.vue_type
        d['id'] = self.id
        d['show'] = self.show
        d['classes'] = self.classes
        d['style'] = self.style
        d['event_propagation'] = self.event_propagation
        d['def'] = self.options
        d['events'] = self.events
        d['width'] = self.width
        d['height'] = self.height
        d['clear'] = self.clear
        d['options'] = self.options
        return d


def my_end(self, msg):
    print(msg)
    self.data = msg.data

async def clear_pad(self, msg):
    self.pad.clear = True
    await msg.page.update()
    self.pad.clear = False
    return True


def pad_test():
    wp = jp.WebPage()
    wp.head_html = '<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>'
    pad = SignaturePad(a=wp, style = 'background-color: white; border: 1px solid;', classes='m-2', onEnd=my_end)
    pad.options = {'penColor': 'blue'}
    clear_btn = jp.Button(text='Clear Pad', classes=jp.Styles.button_simple + ' m-2', a=wp, click=clear_pad)
    clear_btn.pad = pad
    return wp

jp.justpy(pad_test)

Vue.js Component

var signature_pads = {};
    Vue.component('signaturepad', {
        template:
            `<canvas  v-bind:id="jp_props.id" :class="jp_props.classes"  :style="jp_props.style" :width="jp_props.width" height="jp_props.height"></canvas>`,
        methods: {
            pad_change() {
                var id = this.$props.jp_props.id.toString();
                var canvas = document.getElementById(id);
                var signaturePad = new SignaturePad(canvas, this.$props.jp_props.options);
                signature_pads[id] = signaturePad;
                var events = this.$props.jp_props.events;
                var props = this.$props;

                function onEnd() {
                    if (events.includes('onEnd')) {
                        var data = signaturePad.toDataURL('image/png');
                        var point_data = signaturePad.toData();
                        var e = {
                            'event_type': 'onEnd',
                            'id': props.jp_props.id,
                            'class_name': props.jp_props.class_name,
                            'html_tag': props.jp_props.html_tag,
                            'vue_type': props.jp_props.vue_type,
                            'page_id': page_id,
                            'websocket_id': websocket_id,
                            'data': data,
                            'point_data': point_data
                        };
                        send_to_server(e, 'event');
                    }
                }

                signaturePad.onEnd = onEnd;

            }
        },
        mounted() {
            this.pad_change();
        },
        updated() {

            if (this.$props.jp_props.clear) {
                signature_pads[this.$props.jp_props.id.toString()].clear();
            }
        },
        props: {
            jp_props: Object
        }
    });