Drawing with FabricJS and TypeScript Part 9: Saving and Conclusion
Tags: fabricjs, typescript, tutorial, dotnet
Published: 2020-05-27 18:52:48

For the final post in this [series about drawing with FabricJS and TypeScript](https://dev.to/exceptionnotfnd/drawing-with-fabricjs-and-typescript-part-1-intro-goals-and-setup-1imo), let's figure out a way to save our created drawing to the server with an AJAX call.

![](https://dev-to-uploads.s3.amazonaws.com/i/v7tzf0t449jim5nwx6ac.jpg)<figcaption>Get this guy a pencil sharpener!  Photo by [Taru Huhkio](https://unsplash.com/@piipiipikkuinenlintu?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)</figcaption>

##The Sample Project

For the final time, don't forget to check out [the sample project for this series over on GitHub](https://github.com/exceptionnotfound/FabricJSDrawingWalkthrough). It really is quite useful.

##The Final DrawingEditor Modifications

Our save function will need to exist on the Razor Page code-behind file. Because of this, we require a way for our main *DrawingEditor* class to use that function as a callback. We also need to be able to create the JSON for the current drawing, and lucky for us, FabricJS [provides a way to do exactly that](http://fabricjs.com/docs/fabric.Canvas.html#toJSON):

```javascript
class DrawingEditor {
    //...Properties and Constructor
    
    //...Other methods 
    
    setSaveFunc(saveFunction: Function) {
        this.saveFunc = saveFunction;
    }

    save() {
        this.saveFunc();
    }

    getJson(): string {
        //This method serializes the canvas content to JSON
        const outP = this.canvas.toJSON();
        return outP;
    }
}
```

##The Final Markup Modifications

The rest of our changes will take place on our Drawing.cshtml Razor Page. First, we need to add a save button to the markup:

```html
<div class="row drawingToolbar bg-secondary text-light">
    <div class="btn-toolbar">
        <div class="btn-group mr-2" role="group">
            <div id="undoComponent"></div>
            <div id="redoComponent"></div>
            <button onclick="saveDrawing(event); return false;" title="Save" class="btn btn-success">
                <i class="fa fa-save d-inline"></i>
                <span>Save</span>
            </button>
            <div id="deleteComponent"></div>
        </div>
    </div>
    <!--Other components -->
</div>
```

Note the *saveDrawing()* function; this is not implemented quite yet. The next thing we need is to make the [page handler](https://exceptionnotfound.net/using-named-handler-methods-to-make-jquery-ajax-calls-in-razor-pages) that will "process" the POSTed drawing JSON. Since we're not actually doing anything with the JSON, the code is very short:

```csharp
public class DrawingModel : PageModel
{
    [BindProperty]
    public string DrawingContent { get; set; }

    public void OnGet() { }

    public void OnPostSave()
    {
        var drawingJson = DrawingContent;
    }
}
```

In normal circumstances, we'd almost certainly want the *OnPostSave()* method to return something, be it *IActionResult*, *JsonResult*, or just a string.


##The Final Script

Now we need to return to the Razor Page and implement the *saveDrawing()* function to call the new *OnPostSave()* method. We also need to show and hide the "saving" status message on the status bar immediately below the canvas.

One additional thing [Chris](https://www.linkedin.com/in/christopher-jestice) decided to do was put some extra code in to prevent "double-click" saving, so our save method can only be called one second after it was last called. You can see all of this code below:

```javascript
$(function () {
    //...Initialize canvas

    //Set the DrawingEditor's saveFunc
    editor.saveFunc = saveDrawing();
});

function saveDrawing() {
    //If we can save...
    if (!savingLocked) {
        //Show the save message
        showSaving();
        
        //Create the AJAX content
        const drawing = {
            DrawingContent: JSON.stringify(editor.getJson()),
        };

        //POST the content to the server
        $.ajax({
            type: "POST",
            data: drawing,
            async: true,
            url: '?handler=Save'
        }).done(function (data) {
            //When this is done, hide the saving message
            hideSaving();
        }).fail(function () {
            //If this fails, hide the saving message
            //and show an alert
            hideSaving();
            alert('We are unable to save changes at this time.');
        });
    }
}

function showSaving() {
    message = savingMessage;
    $('#StatusBar').html(message);
    savingLocked = true; //Cannot save again for one second
    setTimeout(function () { savingLocked = false; }, 1000);
}

function hideSaving() {
    if (savingLocked)
        //Cannot hide the saving message again for 100 milliseconds
        setTimeout(() => hideSaving(), 100);
    else {
        $('#Saving').remove();
        message = '';
    }
}
```

With all of this in place, we now have a working save feature!

##The Last Hotkey

There's one last little thing we should do: since [we already have hotkeys for cut, copy, paste, etc.](https://dev.to/exceptionnotfnd/drawing-with-fabricjs-and-typescript-part-8-cut-copy-paste-and-hotkeys-6kb) we should implement the hotkey for save (Ctrl+S) as well. Here's the modifications we need to make to *DrawingEditor* to realize this:

```javascript
class DrawingEditor {
    //...Properties

    private keyCodes = {
        'C': 67,
        'S': 83, //New key code
        'V': 86,
        'X': 88,
        'Y': 89,
        'Z': 90
    }

    //...Constructor and other methods

    //Initializes hotkey events
    private initializeKeyCodeEvents() {
        window.addEventListener('keydown', (event: KeyboardEvent) => {
            //process Ctrl Commands
            if (event.ctrlKey) {
                switch (event.keyCode) {
                    case this.keyCodes['Z']:
                        this.undo();
                        break;
                    case this.keyCodes['Y']:
                        this.redo();
                        break;
                    case this.keyCodes['C']:
                        this.copier.copy();
                        break;
                    case this.keyCodes['X']:
                        this.copier.cut();
                        break;
                    case this.keyCodes['V']:
                        this.copier.paste();
                        break;
                    //New key code implementation
                    case this.keyCodes['S']:
                        this.saveFunc();
                        break;
                }

                event.preventDefault();
                event.stopPropagation();
                return false;
            }
        });
    };
}
```

##GIF Time!

Let's see how this works in a GIF:

![](https://dev-to-uploads.s3.amazonaws.com/i/bz99v2ldwrftrneg9ahx.gif)

Pay special attention to the lower-right side of the GIF, where the "Saving" message will appear very briefly.

##Conclusion

That's it! With this last feature, we have made our drawing canvas match [the goal photo from the beginning of the series](https://dev.to/exceptionnotfnd/drawing-with-fabricjs-and-typescript-part-1-intro-goals-and-setup-1imo)! Thank you, dear readers, for reading these posts about creating a drawing canvas with FabricJS and TypeScript! Chris and I are pretty proud of this sample, and we hope it helps you all in your projects.

For the final time, don't forget to check out [the sample project for this series over on GitHub](https://github.com/exceptionnotfound/FabricJSDrawingWalkthrough). It contains the final, working code for this series. Don't be afraid to make pull requests if you see a way we can improve!

Happy Drawing!