So, I guess you have been running wild with Angular lately. If that so, then I’m pretty much sure that you are familiar with the concept of directives. HTML5 too came up with this concept of creating custom components and they call it web components. But today’s lesson is not about web components; rather it is on a third party library that is built upon web components. Like jQuery which is basically a library to work with JavaScript somewhat easily, Polymer is a library that can help us work with the web components easily. Also, it makes web components cross-browser supported. Now, let’s stop there and start making a simple todo app with Polymer.
N.B: I’M USING ASP.NET CORE WEB API TEMPLATE FOR CREATING A SERVER TO HOST THE PROJECT. IN THE NEXT PART OF THIS SERIES, WE WILL SEE HOW TO MAKE APIS AND CALL THEM FROM THE CLIENT SIDE.
In Polymer, component creation starts with defining a template in a <dom-module> tag. It’s a way of saying that we are creating a Local DOM under our Light DOM. Basically, a Local DOM is a way of encapsulating a part of HTML code inside the Light DOM (Light DOM is the basic DOM that we see when we open our developer console in a web browser). Inside Local DOM, the defined HTML area has its own styling and scripting capabilities. When we create a component, we give the name of it as an id to the <dom-module>and initialize it with Polymer like this,
- Polymer({is : "id-of-the-dom-module"});
Okay! Let’s create our first component now. Everybody starts with a component named <hello-world> which is, of course, boring. We will start with creating a component called <hello-universe> which is also boring :D
- <dom-module id="hello-universe">
- <template>
- <style>
- h1 {
- color: blueviolet;
- }
- </style>
- <h1>Hello Universe Component</h1>
- </template>
- <script>
- Polymer({
- is: "hello-universe"
- });
- </script>
- </dom-module>
Okay, so you have your first good for nothing Polymer component. To make that component show up on your main page, you have to make a link to that component inside the <head> tag like this.
- <link ref="import" href="hello-universe.html"/>
Before that, you also have to refer to Polymer itself. To do that, download Polymer or get the Polymer CND link and place it inside the <head> tag. Remember, to make Polymer work, you also have to add the web component script file in your project. So, the final look of your mainpage.html should look like the following.
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <title>Asp.Net Core Polymer Starter Template</title>
-
- <link rel="import" href="lib/polymer/polymer.html" />
-
- <!--Custom Elements-->
- <link rel="import" href="components/hello-universe.html" />
- </head>
- <body>
- <hello-universe></hello-universe>
-
- <script src="lib/webcomponentsjs/webcomponents-lite.js"></script>
- </body>
- </html>
So far so good. But what if you want to inject HTML from your Light DOM to your Local DOM like this,
- <hello-universe>
- <p>Hello</p>
- </hello-universe>
There is a special tag for that. It is the <content> tag. You have to define this tag inside your Local DOM where you want to inject some Light DOM HTML.
- <dom-module id="hello-universe">
- <template>
- <style>
- h1 {
- color: blueviolet;
- }
- </style>
- <h1>Hello Universe Component</h1>
- <content select='p'></content>
- </template>
- <script>
- Polymer({
- is: "hello-universe"
- });
- </script>
- </dom-module>
If you just simply put the <content> your Shadow DOM, it will have all the Light DOM HTML injected directly into your Shadow DOM.
But you can of course filter them. Like if you want to put every single paragraph defined in your Light DOM into your Local DOM then you will have to set an attribute called “select” in your content tag and assigned it to the paragraph tag like this
- <hello-universe>
- <p>Hello</p>
- </hello-universe>
-
- <content select="p"></content>
You can even go further and filter the specific tag on their id, class name and attribute. Like if you want to select a paragraph with an class of description, you can inject that paragraph into your Local DOM like this
- <hello-universe>
- <p class="description">Hello</p>
- </hello-universe>
-
- <content select="p.description"></content>
Like bootstrap, Polymer offers you many components ready to use in your project. Here is the link for the component catalog from where you can install any component of your like. All you have to do is to copy the bowerinstall command for that specific component and run that on your project root. And then you have to refer those component just like you referred your own component.
https://elements.polymer-project.org/
We should have an input component paper-input for our todo application, a template paper-item for showing the added todos in a decent format and other than these we need the button component paper-icon-button and the checkbox component paper-checkbox. Oh! Another thing we need to build our application layout using CSS Flex. So we need iron-flex-layout. And for the icons which will be shown in the button we need iron-icons(Only for styling purpose).Well done!!!
- <link rel="import" href="lib/paper-input/all-imports.html" />
- <link rel="import" href="lib/paper-checkbox/paper-checkbox.html" />
- <link rel="import" href="lib/paper-button/paper-button.html" />
- <link rel="import" href="lib/paper-item/all-imports.html" />
- <link rel="import" href="lib/iron-icons/iron-icons.html" />
- <link rel="import" href="lib/iron-ajax/iron-ajax.html" />
Refer those components like we did before
So, we have all the things we need. Now it’s time to build our own todo component. Let me show you the markup first then ill discuss what’s going on. Here you go
- <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" items="{{data}}" as="todo">
- <paper-item>
- <div id="content-checkbox">
- <paper-icon-button icon="icons:done" alt="check this!" on-tap="deleteTodo"></div>
- <paper-item-body>
- <div>{{todo.title}}</div>
- </paper-item-body>
- <paper-icon-button icon="icons:done" alt="check this!">
- </paper-icon-button>
- </paper-item>
- </template>
- </div>
- <div class="flex4child"></div>
- </div>
- </template>
- <script>
- Polymer({
- is: "simple-todo",
- properties: {
- newTodo: {
- type: String,
- notify: true,
- value: 'make some custom web components'
- },
- data: {
- type: Array,
- value: [],
- notify: true
- }
- },
- addTodo: function () {
- this.push('data', {
- "title": this.newTodo,
- "isDone": false
- });
- this.newTodo = "";
- },
- deleteTodo: function (e) {
- var index = this.data.indexOf(e.model.todo);
-
- if (index !== -1) {
- this.splice('data', index, 1);
- }
- }
- });
- </script>
- </dom-module>
Now this is a very basic shell for creating a todo component. Before going further let’s see if we can get familiar with some of the new construct used in the markup. We are already familiar with the is property, like that we also have a new property called properties. This properties property is used to set properties for the component. In that our first property is newTodo. It’s a String type property and the default value is set to a static text. To make that newTodo property two way bindable, we use notify: true. The same concept goes for data property too. Here it’s an Array property since we want to store the list of todos into it. The initial value is an empty array and it is also set to notify: true. Other important things to notice in the markup is the two-way binding syntax which is denoted by double curly braces {{ }}. Here we put {{newTodo::input}} on the text input attribute value. ::input means that the value will be updated when there is some changes in the text field i.e. typing something. Polymer gives us a dom-repeater template which we can use if we want to generate a defined template over and over again with different data. So here we have an attribute called itemsset to data, means we want to iterate through the items in the data array. The as keyword is used to define an instance name for the items. If we don’t use the as keyword by default your item instances will be called item. In the dom-repeater template we have a checkbox which is bind to the isDone property. A div is used, of which the text content is bind to the title of the todo.
Okay, as you can see. We have our basic todo template but yet we don’t have any events attached to it. We want to add a new todo to the data array when we click on the paper-icon-button button with the add icon on it. So we have an on-tap attribute for that where we can pass a name of the function which will run when user tap on that button. So let’s do that first,
- <paper-icon-button icon="icons:add-circle-outline" suffix on-tap="addTodo"></paper-icon-button>
After attaching to the on-tap attribute a function, it’s time to define it in our script.
- addTodo: function () {
- this.push('data', {
- "title": this.newTodo,
- "isDone": false
- });
- this.newTodo = "";
- }
If you notice carefully, you will see that the array push syntax is kind of different from what we use in the good old javascript. The syntax will automatically update the data array and reflect the changes in the ui. If instead we would have used data.push it would have add the todo to that array but it wouldn’t fire the change event behind the scene which will make the todo array looks like it is updated with new value. Another event will be a single todo delete event. Let’s attach the respective on-tapattribute on the paper-icon-button with the icon of done in the template repeater section.
- <paper-icon-button icon="icons:done" alt="check this!" on-tap="deleteTodo">
Here is the function definition for that
- deleteTodo: function (e) {
- var index = this.data.indexOf(e.model.todo);
-
- if (index !== -1) {
- this.splice('data', index, 1);
- }
- }
Here, first I’m getting the index of the current todo item. The current todo item on which the delete function is called can be found in the e.model.todofield (here e is the function argument). Then we check if the index is greater than -1. If it is so, then we remove the todo by calling the splicefunction on the data array. The third argument to the splice function is for saying how many elements I want to remove from the selected index.
Notice that I’m binding the item.isDone property to the checkbox’s checkedattribute.
We are done with our simple todo. Here is the final look of the main page markup after refering the new <simple-todo> in the markup
We are done with our simple todo. Here is the final look of the main page markup after refering the new <simple-todo> in the markup
- <link rel="import" href="components/simple-todo.html" />
-
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <title>Asp.Net Core Polymer Starter Template</title>
- <link rel="import" href="lib/polymer/polymer.html" />
-
-
- <!--Paper Elements-->
- <link rel="import" href="lib/paper-input/all-imports.html" />
- <link rel="import" href="lib/paper-checkbox/paper-checkbox.html" />
- <link rel="import" href="lib/paper-icon-button/paper-icon-button.html" />
- <link rel="import" href="lib/paper-item/all-imports.html" />
- <link rel="import" href="lib/iron-icons/iron-icons.html" />
- <link rel="import" href="lib/iron-flex-layout/iron-flex-layout.html" />
-
-
- <!--Custom Elements-->
- <link rel="import" href="components/simple-todo.html" />
- </head>
- <body>
- <simple-todo></simple-todo>
- <script src="lib/webcomponentsjs/webcomponents-lite.js"></script>
- </body>
- </html>
If you run your project and go to the page where you defined the todo component. You should have your basic working todo application that looks like this
Let’s make APIs using ASP.NET CORE Web API to be able to Add, Update, Edit and Delete todo items on the server side. Good news is ASP.NET CORE documentation already has an article on that. Here is the link,
https://docs.asp.net/en/latest/tutorials/first-web-api.html
Wait a minute! That doesn’t mean I’m not going to do anything in this post. If you went through that article already then you may have noticed that on the server side they are using a ConcurrentDictionary to store the todo items in the memory. Problem is if I stop the project, all my todo items will be wiped out of the memory. So, we need a persistent data storage system. We could have store them in a simple file system but why don’t we store them in a database?
Our database will be local so there is no additional installation of third party software. If you have Visual Studio 2015 installed then you already have SQLSERVER local databse in your machine. We are going to talk with our database using Entity Framework. Entity Framework is a ORM (Object Relational Mapping) tool. Basically it allows you to map a POCO (Plain Old Class Object) to a database table. To be more precise, think of the POCO(In Entity Framework, the POCOs are called entities) as a shadow to a specific database table. Every property defined in the POCO represents a column in that table. To actually make a POCO represent a database table, you have to pass the type of the POCO in a generic class called DbSet<POCO> and give it a name. Like this for example,
- public DbSet Todos { get; set; }
This statement will create a database table named Todos with columns named after the properties defined in the Todo class. We can execute queries against this Todos property using C# code. No need to use those old ADO.NET queries like: SELECT, UPDATE, DELETE. There can be multiple DbSets which will represent additional tables in the database. But they must be defined in an Entity Framework database context. More clearly, we must create these DbSets in a class and extend the class from DBContextlike this,
- public class TodoContext : DbContext
- {
- public DbSet<Todo> Todos { get; set; }
- }
Let’s do those things I talked about now in our project (If you followed the article on the link I provided earlier, you should have an up and running project by now). Add a new folder in the project and name it Data and create a class under it. I named the class TodoContext since my application is a todo application, or you can choose a name of your like. Code for that class is given below,
- using Asp.Net.Core.With.Polymer.Starter.Models;
- using Microsoft.EntityFrameworkCore;
-
- namespace Asp.Net.Core.With.Polymer.Starter.Data
- {
- public class TodoContext : DbContext
- {
- public TodoContext(DbContextOptions<TodoContext> options) : base(options) {}
-
- public DbSet<Todo> Todos { get; set; }
- }
- }
Remember to add the using statements. In this case DbSet and DbContext is a part of a package called Microsoft.EntityFrameworkCore. To install that package, simply add the package name with the version along with the other packages (under the dependencies node) which are already installed when the project was created,
- "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0"
Before using the Todos DbSet in our TodoRepository (Repositories are responsible for working with data) we have to configure a database connection for the project. Add a ASP.NET Configuration File in the project. By default it will create a ConnectionStrings node where a connection string is placed in the DefaultConnection node.
- {
- "ConnectionStrings": {
- "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true"
- }
- }
Just change the database name before you proceed (it is named todo_db on my end). In the Startup.cs we have to configure our DbContext class to use that default connection. So here it is,
- public class Startup
- {
- public Startup(IHostingEnvironment env)
- {
- var builder = new ConfigurationBuilder()
- .SetBasePath(env.ContentRootPath)
- .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
-
- Configuration = builder.Build();
- }
-
- public IConfigurationRoot Configuration { get; }
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDbContext < TodoContext > (options = >
- options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
-
- services.AddMvc();
- services.AddSingleton< ITodoRepository, TodoRepository> ();
-
- }
-
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
- {
- loggerFactory.AddConsole();
-
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseMvc();
- }
- }
Here in the constructor, we are building a configuration root which eventually stores the configurations defined in the appsetting.json file as a key-value pair. Then in the ConfigureServices, we get the default connection string by its key. Entity framework can work with a wide range of database systems. Since we are using SqlServer here, so we are configuring it in the option of the AddDbContext<TodoContext> with our connection string.
Let’s move on to the TodoRepository. We will add a private TodoContextvariable which we can use to work on the Todos DbSet indside the methods of the repository. Also remove the existing ConcurrentDictionary since we don’t need it anymore. Also in the contructor set the private context variable to the passed in context variable (see code below). The reason of doing this is to have dependency injection in our system. You don’t have to worry about configuring it like we did for the ITodoRepository,
- services.AddSingleton<ITodoRepository, TodoRepository>();
It is already configured to inject a new instance of the TodoContext if we defined a parameter of that DbContext in the constructor
- services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
Now replace everything in the TodoRepository class with the code given below,
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Asp.Net.Core.With.Polymer.Starter.Data;
- using Asp.Net.Core.With.Polymer.Starter.Models;
-
- namespace Asp.Net.Core.With.Polymer.Starter.Repository
- {
- public class TodoRepository : ITodoRepository
- {
- private readonly TodoContext _context;
-
- public TodoRepository(TodoContext context)
- {
- _context = context;
- }
-
- public IEnumerable<Todo> GetAll()
- {
- return _context.Todos;
- }
-
- public void Add(Todo item)
- {
- item.Id = Guid.NewGuid().ToString();
- item.DateAdded = DateTime.Now;
- item.IsDone = false;
-
- _context.Todos.Add(item);
- _context.SaveChanges();
- }
-
- public Todo Find(string id)
- {
- Todo todo = _context.Todos.AsNoTracking().FirstOrDefault(s => s.Id == id);
- return todo;
- }
-
- public Todo Remove(string id)
- {
- Todo todo = _context.Todos.FirstOrDefault(s => s.Id == id);
- _context.Todos.Remove(todo);
- _context.SaveChanges();
-
- return todo;
- }
-
- public void Update(Todo item)
- {
- _context.Todos.Update(item);
- _context.SaveChanges();
- }
- }
- }
As you can see we are doing basic CRUD on the Todos DbSet with the help of the TodoContext. Nothing more to say here because everything is self-explanatory. But always remember to call the SaveChanges() function on the context when you are doing something on the database table and need to save the changes. Without calling it nothing will be changed. Also notice that when we are saving a todo item, we are adding the current date and time within the DateAdded property and generating a new GuId to set on the Id property. The Id is for the todo to have a unique identity in the database (Primary Key). Also, we are setting the IsDone status property of the todo item to false.
Everything looks good! Except for that we didn’t run a migration command on our database. A migration command will create a shadow copy of the current state of the database (tables, schema, indexes, key) in a .cs file. The .cs file name would be the name of the migration with a timestamp added to the front. Run the following command in the package manager window to add a migration named Initial
Add-Migration Initial
If you getting errors, then there is nothing to worry about! Your project is missing some packages like,
Microsoft.EntityFrameworkCore.ToolsMicrosoft.EntityFrameworkCore.SqlServer.Design
After the command is successful, you will have the Initial.cs file with the current timestamp attached to it. Now we need to apply it. So run this command in the package manager window,
update-database
After this is successful, you should have a database in your local database with the name you selected for the database and a new table called Todoslike this,
And we are done! Now we can call our APIs from postman(a REST client) and if we add a new todo it will be added directly into our database. Then of course we can update or delete it if we want with the help of other APIs.
We were done with the API creation part. 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 component 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 JSONdocument. 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 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. Add the package using Nuget.
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