Anatomy of an Azure Function

In a previous post we looked at creating Azure Functions, hopefully from this we’re able to quickly and easily get things up and running. We also looked (in another post) and various triggers and hooks which help run our functions.

For this post I want to look into functions a little deeper.

Template code

As we’ve seen, from the Azure Portal or from Visual Studio (with the relevant extensions) we can easily generate our function code. Different function types have slightly different code patterns.

This post will concentrate on C# code, but we can generated code in JavaScript, F# and more.

Azure Portal Generated Code

The Azure Portal generates code for us, which for a C# developer is actually generating C# scripting code. Hence, unlike via Visual Studio, to reference external assemblies which are not included by default. In this case we can use #r (requires) and the assembly name, for example

#r "System.Data"

It’s fairly obvious that an Azure function is literally that, a function/method. Not a class with multiple methods where you create an instance of the class and interact with it. Hence Azure functions are static and may be async, or not, as required.

Depending upon the type of function we’re creating we may return a response or void or another object type. Again, depending on the type of function, we may have a TimerInfo, or an HttpRequestMessage and there may be other arguments, whether they’re parameters for a query of connections to BlobStorage (via a Stream), Queue names or whatever.

With the Azure Portal functions we also have generated for us, the function.json file (and in some cases a README.md file).

function.json

This file acts as a binding (using JSON as the extension suggests) which defines the inputs and outputs as well as some configuration details, such as whether it’s disabled or not.

Visual Studio Generated Code

Code from Visual Studio differs from the Portal code in that we’re creating assemblies here, we’re not uploading scripts to the Azure Portal. Hence we add references in the standard way to the project. We don’t have a function.json file, but instead have a host.json file as well as a local.settings.json file (which we’re look into later).

Our function name (i.e. the name we see in the Portal etc.) is defined using the FunctioNameAttribute on the method. The configuration that exists in the function.json file becomes attributes on the Run method (generated by Visual Studio). So for example, what type of trigger is often defined as an attribute on the first arguments, for example

[FunctionName("TimerTest")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, TraceWriter log)
{
   log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}

and this attribute then will tend to have the configuration for the trigger, for example this TimerTriggerAttribute has the CRON format string, an HttpTriggerAttribute might have the AuthorizationLevel used, the HTTP methods uses, the Route etc. The BlobTriggerAttribute has connection information etc. Obviously, just like the Azure generated code, other parameters exist depending upon the type of trigger and/or params passed to the method. Also included is the TraceWriter for us to log information to.

For anything other than an HttpTrigger function, you’ll need to supply AzureWebJobStorage details etc. in the local.settings.json file, to do this, from Visual Studio Command Prompt and from your project’s folder (where it’s host.json and local.settings.json files reside) run

func azure functionapp fetch-app-settings (your-app-name)

Replacing your-app-name with the app you will need to have created in the Azure Portal. If you have multiple projects then you have to do this in each one’s project folder. This command will fill in connections strings, account details and encrypted keys etc.

Note: if the func exe is not in your path etc. you can run

npm i -g azure-functions-core-tools

to install the tools.

Running/Debugging locally

Obviously, for as much development as possible, it would be best if, whilst testing, we ran our functions locally. As mentioned, to integrate your Azure Portal app settings we can run

func azure functionapp fetch-app-settings (your-app-name)

and you may have noticed when we run via the debugger the func.exe is executed. So it also makes sense that we can run our binary representations of our functions locally outside of the debugger.

Within your project’s output folder, i.e. bin\Debug\net461 (for example) and obviously after you’ve actually built you project, you can run (via the command prompt or Visual Studio command prompt (where func.exe is in your path) you can run

func host start

This will fire up an Azure emulator, read host.json, generate the host for your functions and then run a service on localhost:7071 (in my case) to host your functions.

Azure function triggers

In a previous introductory post to Azure functions I mentioned that there were more than just the HttpTrigger.

The current list of triggers available (at least for C#/.NET developers) are

  • HttpTrigger – triggers a function based upon an HTTP request, like a REST service call
  • HttpTriggerWithParameters – triggers a function based upon an HTTP request, like a REST service call
  • TimerTrigger – triggers a function based upon a CRON like time interval/setup
  • QueueTrigger – triggers when a message is added to the Azure Queue Storage
  • BlobTrigger – triggers when a blob is added to the specified container
  • EventHubTrigger – triggers when a new event is received via the event hub
  • ServiceBusQueueTrigger – triggers when a message is added to the service bus queue
  • ServiceBusTopicTrigger – triggers when a message is added to the service bus topic
  • ManualTrigger – triggers when the Run button is pressed in the Azure Portal
  • Generic Webhook – triggers when a webhook request occurs
  • GitHub Webhook – triggers when a GitHub webhook request occurs

Many of these are pretty self-explanatory, but let’s have a quick look at some of them anyway.

Note: I’m not going to cover the triggers that require other services, such as queue’s, event hubs, web hooks etc. in this post as they require more than a short paragraph to set-up etc. So I’ll look to dedicate a post to each as and when I begin using them.

HttpTrigger

The HTTP Trigger is analogous to a REST like service call. i.e. when an HTTP request is received the Azure function is called, passing in the HttpRequestMessage object (and a TraceWriter for logging).

Here’s a bare bones function example.

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
   log.Info("C# HTTP trigger function processed a request.");
   return req.CreateResponse(HttpStatusCode.OK, "Hello World");
}

The HttpTrigger supports a lot of HTTP methods, including the obvious GET, POST, DELETE along with HEAD, PATCH, PUT, OPTIONS, TRACE.

Note: Everything we can do with HttpTriggerWithParameters, i.e. changing the HTTP method type, adding a route to the function.json and adding parameters to our method to decode the route parameters, can be implemented with the HttpTrigger. At the time of writing this post, I’m not wholly sure of the benefit of the HttpTriggerWithParameters over HttpTrigger.

HttpTriggerWithParameters

An HttpTriggerWithParameters trigger is similar to an HttpTrigger except when we create one via the Azure Portal, by default we get the following code

public static HttpResponseMessage Run(HttpRequestMessage req, string name, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");
    return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

Notice the return is not wrapped in a Task and includes the string name parameter which, via Azure Portal we need to set the HTTP method to GET. GET is not the only supported method for this function but appears that way by default. To add HTTP methods we need to go to the function’s integrate menu option and add supported HTTP post methods and parameters can be passed in via the Azure portal’s Query parameters. More interestingly though a route is automatically created for us in the associated function.json file. The route looks like this

“route”: “HttpTriggerCSharp/name/{name}”

This obviously means we can navigate to the function using https://(app-name).azurewebsites.net/api/HttpTriggerCSharp/name/{name}

Let’s add an age parameters to the Query using the Add parameter option in the portal. If we change our route to look like this (in the function.json file)

“route”: “HttpTriggerCSharp/name/{name}/{age}”

and the source to look like this

public static HttpResponseMessage Run(HttpRequestMessage req, string name, int? age, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    // Fetching the name from the path parameter in the request URL
    return req.CreateResponse(HttpStatusCode.OK, "Hello " + name + " " + age);
}

we can now navigate to the function using https://(app-name).azurewebsites.net/api/HttpTriggerCSharp/name/{name}/{age}

TimerTrigger

As the name suggests, this trigger is dependent upon a supplied schedule (using CRON format). Obviously this is very useful in situations where we maybe run reports every day at a set time or could be used to periodically check some source (which is not supported as a trigger) such as a file location.

The code generated by the Azure Portal looks like this

public static void Run(TimerInfo myTimer, TraceWriter log)
{
    log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}

ManualTrigger

The manual trigger is a trigger which is executed via the Azure Portal’s Run button. One might view it a little like running a batch file or simply a function which you do not want others to have access to, the code generated by the portal looks like this

public static void Run(string input, TraceWriter log)
{
    log.Info($"C# manually triggered function called with input: {input}");
}

and values are supplied as input (to the string input via the Request Body input box as plain text.

Python REST service using Flask

Wanting to create a Python based REST service? Let’s use Flask and see what we can do.

Start off by installing Flask

pip install Flask

Now let’s write some code

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

app.run()

This is nice and simple and pretty obvious how things work. As you can see we create the Flash application and in this sample run it (although on the Flask website they often show running the Flask Run from the Python command line).

Each “route” is defined and mapped directly to a function. In this example the root of the URL is mapped to the hello function.

The route can also contain variables which are show within <>, for example

@app.route('/<name>')
def hello(name):
    return f"Hello {name}"

We can also define converters (similar to declaring the type of the variable) for the variables, so for example if we need to access something by an integer id, the we might have

@app.route('/employee/<int:id>')
def employee(id):
    return f"Employee {id}"

By default the converter used (as you’d probably expect) is string, but Flask also supports int, float, path, any and uuid.

By default each route is using the HTTP method GET, but we can also define the supported methods for each route, i.e.

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()

References

Flask is Fun

C# 8.0 enhancements with pattern matching

C# 8.0 language features include pattern matching similar to that within F#.

Tuples have become a first class citizen of C# 8.0, so we can write something like this

public (string, int) CreateTuple(string s)
{
   return (s, s.Length);
}

This results in a ValueType being created, which can still be accessed using properties Item1 and Item2, however we can also use tuples within pattern matching, for example

var t = CreateTuple("Ten");
switch (t)
{
   case (string s, 1):
      return $"Single Character {s}";
   case (string s, int l):
      return $"{s}:{l}");
}      

In the above we’re matching firstly against a tuple where the first argument is a string and the second an int with value 1, the second case statements is basically going to catch everything else. Obviously the “catch all” must go after the more specific pattern match.

This can be tidied further by using expressions. So the above becomes

return t switch
{
   (string s, 1) => $"Single Character {s}",
   (string s, int l) => $"{s}:{l}"
};

Properties and fields in TypeScript

There’s several ways to handle properties within TypeScript, let’s look at a simple class

class Point {
    x: number;
    y: number;
}

Whilst Visual Code etc. will say these are properties, they’re more like public fields (if you wish to compare to a language such as C#), hence missing the functional side that comes with properties.

By default no accessors means the properties x and y are public. TypeScript allows us to mark them as private in which case we could then write functional getters and setters as per Java, for example

class Point {
    private x: number;
    private y: number;

    getX() {
        return this.x;
    }
    setX(x: number) {
        this.x = x;
    }
    getY() {
        return this.y;
    }
    setY(y: number) {
        this.y = y;
    }
}

TypeScript also supports C# style property getter and setters like this

class Point {
    private x: number;
    private y: number;

    get X() : number {
        return this.x;
    }
    set X(x : number) {
        this.x = x;
    }
    get Y() : number {
        return this.y;
    }
    set Y(y : number) {
        this.y = y;
    }
}

and like C# these get/set methods result in property style syntax, i.e.

var p = new Point();
p.Y = 4;
console.log(p.Y);

Constructors within TypeScript can reduce the ceremony for creating fields and properties by using the private or public keywords for constructor arguments, for example

class Point {
  constructor(public x: number, public y: number) {
  }
}

// becomes

class Point {
    get x() {
        return this.x;
    }
    set x(x) {
        this.x = x;
    }
    get y() {
        return this.y;
    }
    set y(y) {
        this.y = y;
    }
}

Using private constructor arguments is equivalent private fields, so for example

class Point {
  constructor(private x: number, private y: number) {
  }
}

// becomes

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

If we need to overwrite the getter/setters functionality, we can write

class Point {
    constructor(public x: number, public y: number) {
    }
   
    public get x() : number {
        return this.x;
    }
    public set x(x : number) {
        this.x = x;
    }   
    public get y() : number {
        return this.x;
    }
    public set y(y : number) {
        this.y = y;
    }
}

Redux and storybook

We’ve implemented our React UI and set-up storybook to test it but we’re using redux as our store, so how do we use this in storybook?

Storybook allow us to create a decorator which is really just a wrapper around our story, so for example we add a decorator using .addDecorator like this

storiesOf("SomeComponent", module)
  .addDecorator(withProvider)
  .add("default", () => 
    <SomeComponent />
 );

Within the .addDecorator we can add more React code or HTML, maybe to position our test component centrally in the screen or in this case we can use the same code to wrap a Provider.

As you can see from the above code we’ve got a withProvider value which looks like this

const withProvider = (story) => <Provider store={store}>{story()}</Provider>

Hence the code takes a story (the story we’re testing) and we simply wrap it within a Provider element having previously created the redux store using the standard createStore function, i.e.

const store = createStore(reducer);

Now we can test our UI/UX code within storybook using the redux store for it’s state.

One log decorator to rule them all

One log to rule them all

In a previous post we looked at implementing log decorators using the experimental features of TypeScript.

Obviously it’d be far nicer if we had a single log decorator that we can apply to each place where a decorator might be used, i.e. a log decorator which can be used instead of logClass, logMethod etc. We can simply create a log function which takes any number of arguments and have this delegate to the specific log function as required (a factory function basically). I’m not going to write the whole thing here, but something along the lines of (the following code) would do the job

function log(...args: any[]): void {
  switch(args.length) {
    case 1: 
      // return logClass
    case 2: 
      // return logProperty     
    case 3: 
      return (typeof args[2] === "number") ? 
        logParameter :
        logMethod
      default:
         throw new Error();    
   }
}

inversify

I wanted to use an IoC pattern/library in a project I was working on (a node server application where I would slot in the specific implementations of the data and services at runtime).

I cam across inversify, there may be other IoC’s for TypeScript, but this is the one I looked into and worked a treat for me.

yarn add inversify reflect-metadata

You will need to enable experimentalDecorators and emitDecoratorMetadata within the tsconfig.json file.

"experimentalDecorators": true,        
"emitDecoratorMetadata": true, 

as per the invertsify documentation, we should also have the following tsconfig.json properties set

"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",

As is usual in C# (at least), the best way to work with IoC is to define an interface for our abstraction and then an implementation. However for inverisfy we also define a Symbol for the interfaces. So let’s assume we’re going to have a Service interface along with RestService and WebsocketService.

First create your Service.ts file and here’s a simple example

interface Service {
  call(data: string): void;
};

export default Service;

Now let’s create a couple of implementations, RestService.ts

import "reflect-metadata";
import Service from "./Service";
import { injectable } from "inversify";

@injectable()
class RestService implements Service {
  call(data: string): void {
    console.log("RestService called");
  }
}

export default RestService;

As you can see, we decorate the class with @injectable as this class may be injected into another class.

Here’s our WebsocketService.ts

import "reflect-metadata";
import Service from "./Service";
import { injectable } from "inversify";

@injectable()
class WebsocketService implements Service {
  call(data: string): void {
    console.log("WebsocketService called");
  }
}

export default WebsocketService;

Within the types.ts (below) we create the symbols (keys/identifiers) for use with our types.

const Types = {
  Service: Symbol.for("Service"),
  Application: Symbol.for("Application")
};

export default Types;

Here’s an Application interface Application.ts

interface Application {   
  run(): void;
}

export default Application;

MyApplication.ts

import "reflect-metadata";
import { inject, injectable } from "inversify";
import Application from "./Application";
import Types from "./types";
import Service from "./Service";

@injectable()
class MyApplication implements Application {
  private readonly service: Service;

  constructor(@inject(Types.Service) service) {
    this.service = service;
  }

  run(): void {        
    this.service.call("");
  }
}

export default MyApplication; 

In the above you can see we’re injecting (via the @inject decorator on the constructor) a type of symbol type Service. Hence this is where the server implementation will be injected into.

To supply inversify with the implementations for various types, we create the file inversify.config.ts. In the example file below we bind the symbol type/identifier with an implementation.

import { Container } from "inversify";
import Service from "./Service";
import RestService from "./RestService";
import Types from "./types";
import WebsocketService from "./WebsocketService";
import Application from "./Application";
import MyApplication from "./MyApplication";

const container = new Container();

//container.bind<Service>(Types.Service).to(RestService);
container.bind<Service>(Types.Service).to(WebsocketService);
container.bind<Application>(Types.Application).to(MyApplication);

export default container;

Finally, here’s an example implementation of the index.ts

import container from "./inversify.config";
import Application from "./Application";
import Types from "./types";

const application = container.get<Application>(Types.Application);
application.run();

There’s more to inversify than just the code listed above, such as you can have multiple Service implementations and use named bindings to receive the one you want and more.

JSON within subclasses, across multiple programming languages

I was developing an expression tree on a project, i.e. made up of subclasses of the class Expression, such as AndExpression, MemberExpression, LiteralExpression etc. The main code is TypeScript/JavaScript but this needs to pass JSON to TypeScript/JavaScript, C# or Java code (and possibly other languages).

Now when JavaScript JSON.stringify does it’s thing we’re left with no type information making it problematic converting each type back to it’s actual type, i.e. to an AndExpression not just an Expression.

A relatively easy way to solve this, whilst not as elegant as one might hope is to store a string representing the type within the object, for example

export class LiteralExpression extends Expression {
  public readonly $type: string = "LiteralExpression"
}

When we run the expression through JSON.stringify we get JSON with “$type”:”AndExpression” for example. In JavaScript we still need to do some work to convert this back to JavaScript classes, it’s easy enough to use JSON.parse(json) then iterate over our expression objects converting to subclasses and revive our objects from JSON in this way.

Note the use of the variable name $type. It’s important that it’s named $type if you want it to easily be translated into C# objects with Json.NET as this name is hard coded in this library, whereas Java’s jackson JAR allows us to easily change the name/key used.

Json.NET (Newtonsoft.Json)

Sadly we don’t quite get everything for free using Json.NET because it’s expecting C# style naming for classes, i.e. assembly/namespace etc. The easiest way to deal with this it to serialize/deserialize using our own SerializationBinder, for example

public static class Serialization
{
  public class KnownTypesBinder : ISerializationBinder
  {
    public IList<Type> KnownTypes { get; set; }

    public Type BindToType(string assemblyName, string typeName)
    {
      return KnownTypes.SingleOrDefault(t => t.Name == typeName);
    }

    public void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
      assemblyName = null;
      typeName = serializedType.Name;
    }
  }

  private static KnownTypesBinder knownTypesBinder = new KnownTypesBinder
  {
    KnownTypes = new List<Type>
    {
      typeof(AndExpression),
      typeof(BinaryExpression),
      typeof(LiteralExpression),
      typeof(LogicalExpression),
      typeof(MemberExpression),
      typeof(NotExpression),
      typeof(OperatorExpression),
      typeof(OrExpression)
    }
  };

  public static string Serialize(Expression expression)
  {
    var json = JsonConvert.SerializeObject(
      expression, 
      Formatting.None, 
      new JsonSerializerSettings
    {
      TypeNameHandling = TypeNameHandling.Objects,
      SerializationBinder = knownTypesBinder
    });
    return json;
  }

  public static Expression Deserialize(string json)
  {
    return JsonConvert.DeserializeObject<Expression>(
      json, 
      new JsonSerializerSettings
    {
      TypeNameHandling = TypeNameHandling.Objects,
      SerializationBinder = knownTypesBinder
    });
  }
}

Don’t forget you’ll also need to mark your properties and/or constructor parameters with [JsonProperty(“left”)] especially if you have situations where the names are keywords.

com.fasterxml.jackson.core

In Java we can add the following dependency to our pom.xml

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.10.0</version>
</dependency>

Now in our Expression base class we write the following

import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME, 
  include = JsonTypeInfo.As.PROPERTY, 
  property = "$type")
public class Expression {
}

This tells jackson to include the type info using the keyword $type.

We also need to add the @JsonCreator annotation to each of our classes and each constructor parameter requires the following annotation @JsonProperty(“left”). Finally to serialize/deserialize we create an ObjectMapper to allow us to map our types to real objects using

package com.rbs.expressions;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;

public class Serialization {

  private static ObjectMapper createMapper() {
    ObjectMapper mapper = new ObjectMapper();

    mapper.registerSubtypes(
      new NamedType(AndExpression.class, "AndExpression"),
      new NamedType(BinaryExpression.class, "BinaryExpression"),
      new NamedType(OrExpression.class, "OrExpression"),
      new NamedType(LiteralExpression.class, "LiteralExpression"),
      new NamedType(LogicalExpression.class, "LogicalExpression"),
      new NamedType(MemberExpression.class, "MemberExpression"),
      new NamedType(NotExpression.class, "NotExpression"),
      new NamedType(OperatorExpression.class, "OperatorExpression")
    );

    return mapper;
  }

  public static String serialize(Expression expression) throws JsonProcessingException {
    return createMapper().writeValueAsString(expression);
  }

  public static Expression deserialize(String json) throws JsonProcessingException {
    return createMapper().readValue(json, Expression.class);
  }
}

Vue components

In a previous post we looked at creating a Vue application. Let’s now look a little more in depth by looking at creating Vue components.

Note: I will be using TypeScript and classes for this, so called “Class-Style component”.

A Vue component would be stored within a .vue file, hence MyComponent.vue for example. Vue components take the form of a template which is made up of HTML with embedded Vue values etc. Styles may also be defined within a style element which will hold standard CSS). Next up we’d have a script tag if our component does anything more than just being a template with or without styles.

Hence we might layout our component like this

<template>
  <div class="hello">
    <h1>{{ greeting }} World</h1>
  </div>
</template>

<style scoped>
h1 {
  color: red;
}
</style>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  private greeting: string = 'Hello';
}
</script>

What we’ve done is create a template for our HTML which is ultimately a div with an h1 element with the text Hello World being displayed. Obviously the greeting part is supplied by the component as it’s wrapped in the {{ }} (Mustache) syntax.

Next we’ve got the style (these sections need not be in any specific order). We obviously don’t need a style nor script if no style or logic is required. However to demonstrate it’s usage we’ve got it here and style scoped to the component (i.e. not global). Ofcourse this will display our template text in H1 font and red text colour.

Now to the slightly more interesting bit. The script contains our code. First off we import the Component decorator and the Vue base class in the standard way.

Obviously as Vue uses the @Component decorators you’ll need the experimental features of TypeScript enabled, which is what the vue client did when creating our application.

As is pretty obvious, we derive our component from Vue and then implement data and methods. In this case we simply have a constant string with our greeting, before we make this editable, let’s look at how we render/use this component.

In our App.vue for example, we add the following to the app template, i.e.

<template>
  <div id="app">
      <MyComponent />
  </div>
</template>

The next bit is dependent upon whether you’re using a router or not. We need to register our component, so without a router our App.vue would contain the following script tag

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import MyComponent from './components/MyComponent.vue';

@Component({
  components: {
    MyComponent,
  },
})
export default class App extends Vue {}
</script>

If we’re using a router we probably be using views and hence would declare the following with the view (here’s an example view)

<template>
  <div class="home">
    <MyComponent />
  </div>
</template>

<script>
import MyComponent from '@/components/MyComponent.vue';

export default {
  name: 'home',
  components: {
    MyComponent,
  },
};
</script>

Adding properties

Now we’ve seen how to create a component we’ll want to extend it by adding properties (attributes in HTML terms). So for example, instead of fixing our greeting string to “Hello” we’ll let the user of our component set the greeting.

To do this we need to add the Prop decorator to the greeting string with MyComponent, i.e.

<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  @Prop() private greeting!: string;
}
</script>

and now from the code using MyComponent we would write

<MyComponent greeting="Hello" />

Adding methods/handlers

Let’s extend our component with a button which, when clicked, increments a counter. So our component script now looks like this

import { Component, Vue, Prop } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  private count: number = 0;

  @Prop() private greeting: string = 'Hello';

  handleClick(): void {
    this.count = this.count + 1;
  }
}

Now our template is changed to this

<template>
  <div class="hello">
    <h1>{{ greeting }} World</h1>
    <button @click="handleClick">Click</button>
    <h1>{{ count }}</h1>
  </div>
</template>

Note: We can use the @click or v-on:click – the @ is just a shorthand for v-on: in this instance.