Jump to content
11 April 2022 Reading: 6 minutes

Building a Bowls Fixtures Component with Vue

One of my latest projects was to create a fixture component for a Bowls Club.

Building a site for a small sports club is never a simple feat. However I worked with St.Chads Bowls Club to create a brand new site. One of the features I wanted to implement was a fixture component which showcased their upcoming fixtures in a modern and visually pleasing way.

The only aspect I had to think about was how these fixtures were created.

The old way

Previously their site was on WordPress, and they used TablePress to convert a CSV into a Table, which they could them embed onto a page.

While this was fine, it wasn't responsive or visually appealing. It was also really difficult to read, with a lot of information being compressed into a table row.

TablePress

Instruments needed

Not only did this need a nicer design, I needed to think of a way to make sure the client could use a CSV to continue creating the fixtures.

Using Craft CMS I installed the following plugins


  • FeedMe - A great import tool which I could import the CSV and map the rows to create entries.
  • Element API - A free Craft Plugin to create a JSON API which I could use with Vue to create the fixture list.

With both of these plugins I could create a structure for Fixtures and import all of the CSV data into individual entries using FeedMe, and then create an API of these using Element API.

Then I could get started on creating the fixture component!

Creating the API

With Element API creating an API Url is very simple. All I needed to do was create a config file called `element-api.php` and then return the new endpoint.

PHP

return [
    'endpoints' => [
        'api/bowls-fixtures.json' => function() {
            return [
                'elementType' => Entry::class,
                'criteria' => [
                    'section' 	=> 'fixtures',
					'orderBy'	=> 'fixtureDate asc',
                    'offset'	=> 0,
					'fixtureType' => fixtureType(),
                ],
                'elementsPerPage' => -1,
                'cache' => false,
                'transformer' => function(Entry $entry) {
                    return [
                        'title'			=> $entry->title,
						'monthYear'		=> date_format($entry->fixtureDate, 'F Y'),
						'date'			=> date_format($entry->fixtureDate, 'M dS Y'),
						'opponent'		=> $entry->opponent,
						'competition'	=> $entry->competition,
						'venue'			=> $entry->venue,
						'rinks'			=> $entry->rinks,
						'dress'			=> $entry->dress,
						'time'			=> $entry->fixtureTime ? date_format($entry->fixtureTime, 'H:i') : 'TBC',
						'type'			=> $entry->fixtureType,
						'rinksUsed'		=> $entry->rinksUsed,
						'shortMonth'	=> date_format($entry->fixtureDate, 'M'),
                    ];
                },
            ];
        },
    ]
]

In regards to the criteria for the API, I wanted to make sure it was ordered by the fixture Date, and make sure only fixtures with a FixtureType were returned. This was done by adding the following function to the file, and the linking it to the API.

PHP

function fixtureType()
{
    $param = Craft::$app->request->getQueryParam('type') ? : null;
    if (!empty($param)) {
        return $param;
    }
}

Once this was built, I could access the API from the endpoint I'd created and then feed this into Vue.

Enter VueJS

I wanted to make use of Vue's Single File Components for the Fixtures. This was because I could break each Fixture Block into a seperate component, and also any additional information. This gave me a cleaner structure to work with.

Firstly I needed to get the data from the API I created before. To do this I used Vue's created event to make sure the API was called everytime the instance was made.

JAVASCRIPT

created: function () {
	let url = this.getUrl();
	this.$http.get(url).then(function (response) {
		this.fixtures = response.body.data;
	});
},

This also used a custom method in which I called the API endpoint. As I wanted the user to be able to filter the fixtures based on types, I had to make sure this worked for other events as well.

JAVASCRIPT

getUrl: function (type) {
	if (type && type !== "All-Games") {
		return "/api/fixtures.json?type=" + type;
	} else {
		return "/api/fixtures.json";
	}
},

The data within my Vue instance was simple, and consisted of the different types of fixtures, the default selected type and the fixtures themselves (which were populated by the API)

JAVASCRIPT

data: function () {
	return {
		types: [
			{
				index: "Mix",
				value: "Mixed",
			},
			{
				index: "M",
				value: "Mens",
			},
			{
				index: "L",
				value: "Ladies",
			},
			{
				index: "Outing",
				value: "Outing",
			},
		],
		typeSelected: "All Games",
		fixtures: [],
	};
},

I also wanted the user to be able to filter the fixtures by month. I was able to do this by getting the month from each fixture, and creating a computed property.

JAVASCRIPT

monthsList() {
	const months = [];
	this.fixtures.forEach((item) => {
		if (!months.includes(item.shortMonth)) {
			months.push(item.shortMonth);
		}
	});
	return months;
},

Using this data, and a few other methods (I can't give away all my secrets!) I was able to create a filterable fixture list which was more accessable, readable and modern.

The Result

You can see the end result on the St.Chads Bowls Website.