Vel Eous
Vel Eous

Reputation: 131

Using Vue.js to bind value of input field to a second input field

I have Symfony Form which defined as below (minus non-pertinent fields for brevity):

<?php

namespace AppBundle\Form;

use AppBundle\Entity\Category;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CategoryType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('label', TextType::class, [
                'label' => 'label.display_name',
                'attr' => [
                    'placeholder' => 'placeholder.category_name',
                    'class' => 'label',
                    '@input' => 'vUpdateSlug'
                ]
            ])
            ->add('slug', TextType::class, [
                'label' => 'label.slug',
                'attr' => [
                    'class' => 'slug',
                    '@input' => 'vUpdateSlug',
                    ':value' => 'slug'
                ]
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Category::class,
            'category_id' => null
        ]);
    }
}

I am attaching Vue.js directives to both input fields. The idea is that when someone types in the label field, the slug field is automatically updated with the label input value with some minor changes (replacing spaces with hyphens). I still want the user to be able to alter the slug if they wish, but not have the label update.

The v-on:input/@input directive behaviour works, however, I'm just starting with Vue.js and my implementation feels a little clunky (repetitive) - see below:

new Vue({
    delimiters: ['[[', ']]'],
    el: '#category-form',
    data: {
        slug: this.slug = $('[name="category[slug]"]').val()
    },
    ready: function () {
        this.slug = $('[name="category[slug]"]').val();
    },
    methods: {
        vUpdateSlug: function (event) {
            var str = event.target.value.replace(/[^a-zA-Z0-9 -]/g, '').replace(/\s+/g, '-').toLowerCase();
            return this.slug = str;
        }
    }
});

Is there a better solution to my problem?

Upvotes: 2

Views: 1107

Answers (1)

Vel Eous
Vel Eous

Reputation: 131

After more researching and tinkering, I came up with the following which produces the desired result:

CategoryType

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('label', TextType::class, [
            'label' => 'label.display_name',
            'attr' => [
                'placeholder' => 'placeholder.category_name',
                'class' => 'label',
                'v-model' => 'label'
            ]
        ])
        ->add('slug', TextType::class, [
            'label' => 'label.slug',
            'attr' => [
                'class' => 'slug',
                '@input' => 'setSlug',
                ':value' => 'slug'
            ]
        ]);
}

Vue Script

new Vue({
    delimiters: ['[[', ']]'],
    el: '#form-wrapper',
    data: {
        label: $('[name="category[label]"]').val(),
        slug: $('[name="category[slug]"]').val()
    },
    watch: {
        label: function(newLabel) {
            this.slug = this.compileSlug(newLabel)
        }
    },
    methods: {
        compileSlug: function(input) {
            return input.replace(/[^a-zA-Z0-9 -]/g, '')
                .replace(/\s+/g, '-')
                .toLowerCase();
        },
        setSlug: function (input) {
            this.slug = this.compileSlug(input.target.value)
        }
    }
});

JSFiddle functioning example

Upvotes: 1

Related Questions