Building Apps with Polymer and ASP.NET CORE (Part - III)

We were done with the API creation part in the previous post. Let’s finish up the app by connecting the simple-todo component with those APIs. Basically we need to call our APIs to do that. We are going to do that with a Polymer component called iron-ajax. It makes Ajax easier to work with. So, just like what we did for every third party components lets install iron-ajax with Bower. Then add it to the header section of your HTML document. Here is the command to install iron-ajax


bower install --save PolymerElements/iron-ajax

After referring the component, let’s configure it to make a GET request to our API. It’s pretty much easy and there is no coding involved in the process. Add this markup to initiate a GET request to get all the todo items from the database in response,


<iron-ajax auto
           url="http://localhost:65342/api/todo"
           handle-as="json"
           on-response="handeleGet"></iron-ajax>

The auto attribute will automatically initiate the Ajax GET call when the component is loaded. Here the url is the URL of the API that we exposed from our backend. The handle-as attribute will treat the response as a JSON document. Notice that in the on-response attribute we have attached a function called handleGet. In many cases that won’t be needed. The reason behind attaching the function is I want to do some modifications on the response data within that function. You can just bind an Array instead with a attibute called last-response if you don’t need any modifications on the response data. Here is the handleGet function definition,


handeleGet: function (data) {
     this.data = data.detail.response.map(function (item) {
         return {
             id: item.id,
             title: item.title,
             isDone: item.isDone,
             dateAdded: moment(item.dateAdded).format('LLL')
         }
     });
}

Here, you can see that I’ve done some modifications on the response data using the javascript map function. We need to show the date of the added todos in a good easy to read format to our users. What I’ve done here is I’ve used a library called moment.js. It’s a very popular library to work with date and time. So, feel free to download and add the moment.js script in the bottom of the body tag.

Next we are going to add the same thing for a POST request. Here is the component markup for that,


<iron-ajax id="ajaxPost"
           url="http://localhost:65342/api/todo"
           handle-as="json"
           body='{{body}}'
           method="POST"
           content-type="application/json"
           on-response="handlePost"></iron-ajax>

For the POST, PUT, DELETE requests we can’t just automatically fire the requests like what we did for the GET request. So from now on we will add a unique id to each of the iron-ajax element so that we can refer them in the code and work with them from there. The short cut syntax for getting an element by id in Local DOM using Polymer is to use the $ sign like this,


this.$.id_of_the_element

To add a new todo through our API we need to wrap the information related to that todo in the POST request body and send it. We have bound the body attribute with an object property called body. When we put something in the todo title textbox, this object just got updated with that value. So when we do a POST it’s just post the title wrapped in a JSON object like this,

self.body = { "title": this.newTodo };

We want to post todo when a user type something on the textbox and click on the add icon button. So I modified the event of that button which now looks like this,


addTodo: function (e) {
    var self = this;
    self.$.ajaxPost.url = "http://localhost:65342/api/todo/";
    self.body = { "title": this.newTodo };
    self.$.ajaxPost.generateRequest();
}

When the POST is successful the response handler will get called. Here is how the handler function looks like


handlePost: function (data) {
    var self = this;
    if (data.detail.status === 201) {
        var createdTodo = {
            id: data.detail.response.id,
            title: data.detail.response.title,
            isDone: data.detail.response.isDone,
            dateAdded: moment(data.detail.response.dateAdded).format('LLL')
        }
        self.push('data', createdTodo);

        self.newTodo = '';
    }
}

About the PUT, we just want to update the isDone property which is bound to the checkboxes of the todo items. Markup for the iron-ajax PUT is given below


<iron-ajax id="ajaxPut"
           handle-as="json"
           method="PUT"
           body='{{body}}'
           content-type="application/json"></iron-ajax>


The attached event handler for the checkbox’s on-change event looks like below,


updateTodo: function (e) {
    var self = this;
    self.$.ajaxPut.url = "http://localhost:65342/api/todo/" + e.model.item.id;
    self.body = { "id": e.model.item.id, "title": e.model.item.title, "dateAdded": moment(e.model.item.dateAdded).format("YYYY-MM-DD HH:mm:ss"), "isDone": e.model.item.isDone };

    self.$.ajaxPut.generateRequest();

}

Notice that we have to format the dateAdded property back to a format which .NET can understand. So we used moment.js again to make our developing life easy.

Last one is the DELETE request. We have to send a DELETE request to the API when someone clicks on the done button for a specific todo item. Markup for the iron-ajax DELETE is given below


<iron-ajax id="ajaxDelete"
           handle-as="json"
           method="DELETE"
           content-type="application/json"></iron-ajax>

The attached on-tap event handler for the button with the done icon on it looks like below,


checkOutTodo: function (e) {
    var self = this;
    self.$.ajaxDelete.url = "http://localhost:65342/api/todo/" + e.model.item.id;

    self.$.ajaxDelete.generateRequest();

    var index = self.data.indexOf(e.model.item);

    if (index !== -1) {
        self.splice('data', index, 1);
    }
}

We are done with the simple-todo component and we know how we can refer it in our main page. But you may ask me that how can I serve statics pages in ASP.NET CORE? So here is how you can achieve that,

We basically need a dependency for serving static files (Microsoft.AspNetCore.StaticFiles) in ASP.NET CORE. Open up the project.json file and update the dependencies node like below,

Then open Startup.cs and add these following lines of code in the Configure method


app.UseDefaultFiles();
app.UseStaticFiles();

And you are done! Just create a index.html file in the wwwroot folder and add all the markups for loading the simple-todo component there. Now if you run your application the index.html page will be served by default and you will see your custom component too

Here is the final markup of the simple-todo component.


<dom-module id="simple-todo">
    <template>
        <style is="custom-style">
            .flex-horizontal-with-ratios {
                @apply(--layout-horizontal);
            }

            .flex4child {
                @apply(--layout-flex-4);
            }

            #content-checkbox {
                display: -ms-flexbox;
                display: -webkit-flex;
                display: flex;
                -ms-flex-direction: row;
                -webkit-flex-direction: row;
                flex-direction: row;
                -ms-flex-align: center;
                -webkit-align-items: center;
                align-items: center;
                width: 56px;
            }
        </style>
        <div class="container flex-horizontal-with-ratios">
            <div class="flex4child"></div>
            <div class="flex4child">
                <paper-input-container>
                    <label>what do you wanna do next?</label>
                    <input is="iron-input" value="{{newTodo::input}}" />
                    <paper-icon-button icon="icons:add-circle-outline" suffix on-tap="addTodo"></paper-icon-button>
                </paper-input-container>
                <template is="dom-repeat" id="domRepeat" items="[[data]]">
                    <paper-item>
                        <div id="content-checkbox">
                            <paper-checkbox checked="{{item.isDone}}" on-change="updateTodo"></paper-checkbox>
                        </div>
                        <paper-item-body two-line>
                            <div>{{item.title}}</div>
                            <div secondary>
                                added, <em>{{item.dateAdded}}</em>
                            </div>
                        </paper-item-body>
                        <paper-icon-button icon="icons:done" alt="check this!" on-tap="checkOutTodo">
                        </paper-icon-button>
                    </paper-item>
                </template>
            </div>
            <div class="flex4child"></div>
        </div>

        <iron-ajax auto
                   url="http://localhost:65342/api/todo"
                   handle-as="json"
                   on-response="handleGet"></iron-ajax>

        <iron-ajax id="ajaxPost"
                   url="http://localhost:65342/api/todo"
                   handle-as="json"
                   body='{{body}}'
                   method="POST"
                   content-type="application/json"
                   on-response="handlePost"></iron-ajax>


        <iron-ajax id="ajaxDelete"
                   handle-as="json"
                   method="DELETE"
                   content-type="application/json"></iron-ajax>

        <iron-ajax id="ajaxPut"
                   handle-as="json"
                   method="PUT"
                   body='{{body}}'
                   content-type="application/json"></iron-ajax>
    </template>
    <script>
        Polymer({
            is: "simple-todo",

            properties: {
                newTodo: {
                    type: String,
                    notify: true,
                    value: 'make some custom web components'
                },
                body: {
                    type: Object,
                    notify: true
                },
                data: {
                    type: Array,
                    value: [],
                    notify: true
                },
                listeners: {
                    "delete": "checkOutTodo"
                }

            },

            handleGet: function (data) {
                var self = this;
                self.data = data.detail.response.map(function (item) {
                    return {
                        id: item.id,
                        title: item.title,
                        isDone: item.isDone,
                        dateAdded: moment(item.dateAdded).format('LLL')
                    }
                });
            },

            handlePost: function (data) {
                var self = this;
                if (data.detail.status === 201) {
                    var createdTodo = {
                        id: data.detail.response.id,
                        title: data.detail.response.title,
                        isDone: data.detail.response.isDone,
                        dateAdded: moment(data.detail.response.dateAdded).format('LLL')
                    }
                    self.push('data', createdTodo);

                    self.newTodo = '';
                }
            },

            addTodo: function (e) {
                var self = this;
                self.$.ajaxPost.url = "http://localhost:65342/api/todo/";
                self.body = { "title": this.newTodo };
                self.$.ajaxPost.generateRequest();

            },

            checkOutTodo: function (e) {
                var self = this;
                self.$.ajaxDelete.url = "http://localhost:65342/api/todo/" + e.model.item.id;

                self.$.ajaxDelete.generateRequest();

                var index = self.data.indexOf(e.model.item);

                if (index !== -1) {
                    self.splice('data', index, 1);
                }

            },

            updateTodo: function (e) {
                var self = this;
                self.$.ajaxPut.url = "http://localhost:65342/api/todo/" + e.model.item.id;
                self.body = { "id": e.model.item.id, "title": e.model.item.title, "dateAdded": moment(e.model.item.dateAdded).format("YYYY-MM-DD HH:mm:ss"), "isDone": e.model.item.isDone };

                self.$.ajaxPut.generateRequest();

            }
        });
    </script>

</dom-module>

Now if you run the app, you will have a working todo application with a database connected to it

Download Source Code

Here is the github repository link of the project

https://github.com/fiyazbinhasan/Polymer-With-ASP.NET-CORE-WEB-API