This project is a simple application using angular materials and ts. The following is a guide I created in order to create the application for oneself. The purpose of the application is to add tasks, then using a controller it will build a “task” based on the html template. All tasks will have the same structure but be independent entities such that you can delete a single task without effecting other tasks.
Step 1: Install node module packages, many websites give basic starting code
https://angular.io/docs/ts/latest/quickstart.html
Step 2: Create files
In order to get everything working in this example we will need to create new files and folders
under the src/app directory. The reasons for having these files and folders will be explained. Right now lets set up having the same basic structure to follow along easier. These files and naming conventions is strickly for the tutorial purposes. You will notice that each html file has a similar named ts file. This is a good convention to follow to keep track of what html file is hoocked up to what ts file.
Folders I created: mainComp, summary, task
Files:
a. Inside of mainComp folder: mainComp.html mainCompController.ts mainCompService.ts
b. Inside of summary folder: summary.html summaryController.ts
c. Inside of task folder: task.html taskController.ts
In Angular, a Controller is defined by a JavaScript constructor function that is used to augment the Angular Scope.
Use controllers to:
- Set up the initial state of the
$scope
object. - Add behavior to the
$scope
object.
Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.
Angular services are:
- Lazily instantiated – Angular only instantiates a service when an application component depends on it.
- Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
Step 3: Define the module if it is a controller (the file name says controller in it) as .controllers if it is a service use .service (the file name has the name service in it) For your ts files only
module AngularTemplate.Controllers{
}
module AngularTemplate.Service {
}
The module name in this example is AngularTemplate because during the set up phase I made AngularTemplate a connection to angularApp. Here is an example to set it up for yourself:
export const angularApp = angular.module(SPECIAL_NAMES.APPLICATION_MODULE_NAME, SPECIAL_NAMES.ANGULAR_DEPENDENCIES)
.config(config)
.run(run);
Step 4: AngularTemplate in this case is the module. Services and Controllers are part of the AngularTemplate will need
to be uniquely identified
mainCompController.ts: export class MainAreaController { }
mainCompService.ts: export class MainService {}
summaryController.ts: export class SummaryAreaController {}
taskController.ts: export class TaskController {}
In side of the {} you can star defining the functions you need
mainComp is where tasks and summary is controlled from. Thus, making a service to deal with talking to tasks and summary makes sense. A service is not really needed since the application is small but gives general information on how to utilize one in the future for larger applications.
Step 5: Setting up the controller for use. This will allow you to write html in a seperate file
as seen in templateUrl and inject it into another html file by simply calling the componet name.
In this case the name is: mainComponent and you would use the component like the following: <main-component></main-component>
AngularTS does not like capitals so it replaces a captical letter with -<lower case letter or the capital letter> as seen above.
//For controller
angularApp
.component(“mainComponent”, {
templateUrl: “mainComp/mainComp.html”,
//note that the templateUrl is dependent on your html file name and folder name
bindings: { },
controller: MainAreaController,
controllerAs: “$ctrl”
});
Controller name must match the controller name specified. When naming I like to add in what the name is for in the name, for example mainComponent is the component name, MainAreaController is the controller name and also add in what it is controlling in these examples its controlling the main.html page so I added in the word main.
//Using controllerAs: “$ctrl” is the same as not having it in as it is the defualt setting
//For Service
angularApp
.service(“Service.MainService”, MainService);
Step 6: You need to inject a scope in order to get information from other controllers or services
In this case we only have 1 Service and 2 controllers. Both controllers will need to
subscripe/talk to the Service for information
This should be injected inside of the module:
module AngularTemplate.Controllers{
static $inject = [“$scope”, “Service.MainService”];
}
Using static $inject will allow to make a “connection” between the controllers and service
static $inject will be needed to all controllers that want to talk to the Service.
In this example the Service will contain all the information needed for the controllers.
A service can inject another service. If another service is taking care of seperate data it needs.
Do not make a circular dependancy. You do not want serviceA to inject serviceB and serviceB to inject
serviceA.
If you are not injecting any scope you can simply put: static $inject = [];
Step 7: Constructor
constructor() {
}
If you are injecting a Scope from step 5 then in your constructor you will need to identify the scope
constructor(private $scope: angular.IScope){}
For the mainAreaController Service.MainService was also injected so the final constructor is:
constructor(private $scope: angular.IScope, private mainService: Service.MainService) {}
The other Controllers will follow the same layout as mainAreaController in this example.
Step 8: “Desctuctor” known as dispose
Though, we will not be using dispose in this example we will put it in for future use:
–every conroller
export class MainAreaController {
static $inject = [“$scope”, “Service.MainService”];
constructor(private $scope: angular.IScope, private mainService: Service.MainService){
this.$scope.$on(“$destroy”, this.dispose);
}
private dispose = () => {}
MainService will not need a distructor since we do not have a scope to destroy on.
Step 9: Inside of mainComp.html we can make a header to start seeing some actual results
<h2> Hello Task </h2>
Step 10: Now if we go to our main html page already created in the template under src/index.html
We will call our mainCompoent as stated in step 5 by using: <main-component></main-component>
<!DOCTYPE html>
<html>
<head>
<!–<base href=”/”>–>
<link href=”/favicon.ico” rel=”shortcut icon” type=”image/x-icon” />
<title>Angular Template</title>
<script src=”<!– @echo LIBRARY_BUNDLE_PATH –>”></script>
<script src=”<!– @echo TEMPLATE_BUNDLE_PATH –>”></script>
<script src=”<!– @echo APPLICATION_BUNDLE_PATH –>”></script>
<link href=”<!– @echo CSS_BUNDLE_PATH –>” rel=”stylesheet” type=”text/css”>
</head>
<body>
<main-component></main-component>
</body>
</html>
Step 11: Displaying the results
There is a readme.txt file that will go over all the instructions for getting running.
It is best to uninstall and reinstall Node.js to get v.6 or higher. If you have version 6 you may still need to install
Node.js on your account. The readme.txt file will have all the instructions on how to install everything properly.
Here is a quick break down on how to uninstall and install:
1. Uninstall gulp from Control Panel: Programs and Features
2. Find Node.js and Uninstall
3. Go to: https://nodejs.org/en/download/current/
4. Make sure you are downloading v6.0.0
5. Use button Current latest Features
6. Then Windows Installer
7. Use .exe file downloaded
8. Go to cmd inside your angular project (The angular template given)
9. Type “npm install”
10. Type “npm install tsd –global”
11. Type “npm install gulp –global”
Step 12: Using gulp
If your enviroment is now set up properly you should be able to type: gulp serve
In the command line while in the main working directory of your project
If gulp serve does not work try: angular_module\.bin\gulp serve
Gulp would not be working if it is not installed globally correctly. Seek help
After gulp serve keep the command window open and open up anther command window
In this window you will type: gulp watch
With gulp watch it will track changes to files you make. If you add new files or folder use ctrl-c and say Yes by typing Y into the command window
This will terminate the gulp watch session
After you stopped the session you can use gulp watch again
The command: gulp build
Is used to build the file and it will not look for changes while gulp watch will build and track changes on certain file types such as: .less .html .ts
Great! You can now get to work on the actual application. 🙂
Phase 2: Getting to buisness, adding tasks
Step 1: Somewhere to hold tasks created
We are going to want to recieve and store tasks. We will create an array of not just tasks but components. Think of link list nodes.
export interface ITask {
id: number;
name: string;
}
Currently we only are interested in the name of the task but we can give each task an ID in order to track the tasks effeciently. (more details to come)
Id can be set by a ID counter that will sign a unique ID based on the order added:
private idCounter = 0;
When a task is added you will increment the idCounter.
Step 2: Creating ITask array
public tasks: ITask[] = [];
public is so that everyone has access to the array. tasks will be the name of the array. Lastly the task array is of type ITask and will be initialied to empty.
Step 3: Starting off with some data
constructor() {
this.addTask(“task1”);
this.addTask(“task2”);
}
Step 4: Creating the addTask functions
public addTask = (newTask) => {
this.tasks.push({ id: this.idCounter, name: newTask });
this.idCounter++;
}
We need to remember to increment the idCounter in order to ensure that all tasks added to the array will not have the same ID.
Step 5: Connecting tasks to taskController
Inside of taskController.ts you will want to add in a function to collect tasks from mainService
public get tasks() {
return this.mainService.tasks;
}
Now when you reference tasks in your task.html it will grab the tasks within mainService
Step 6: Adding in taks from input box
Adding and removing tasks will be managed by our taskController
Inside of taskController.ts add in a new variable:
private newTask: string = “”;
We can now give this newTask variable to mainSernice to add to our ITask array
public addTask = () => {
this.mainService.addTask(this.newTask);
this.newTask = “”; //clears newTask for next newTask to be added
}
Step 7: Making a input box and button to see add tasks
<input type=”text” ng-model=”$ctrl.newTask”>
<md-button class=”md-raised” ng-click=”$ctrl.addTask()”> Add Task </md-button>
For the input box we said the type of input would be text. We can make the type of input be checkbox but that would
only get us true and false values. ng-model is used to specify where the data inside of the text will be stored.
class is just for styling the button in addition md-button is just a fancier button aposed to button
ng-click is the event that will occur once the button is clicked
In this case we are calling the funciotn addTask inside of the current controller being taskController: $ctrl.addTask()
Step 8: Displaying the names added
ng-repeat is used to repeat tasks. Still inside of task.html you can add the following code segment:
<div ng-repeat=”t in $ctrl.tasks track by $index”>
{{ $ctrl.tasks[$index].name }}
</div>
Since our structure ITask we can track the elements in tasks by id. We can track the tasks by index which will track
based on the elements location in the array. Leading do replacing $ctrl.tasks[$index] with t and by ng-repeaset becoming “t in $ctrl.tasks track by t.id”
<div ng-repeat=”t in $ctrl.tasks track by t.id”>
{{ t.name }}
</div>
This way we can ensure we make changes to the DOM elements and not just updating the array in a specific location but completly replace it.
Phase 3: Removing a Task
Step 1: Making remove button
We will need a way to tell if we want a item to be deleted or not. This can be done simply by adding a button and when it is clicked you remove that item.
Inside of task.html add:
<md-button class=”md-fab md-mini”
ng-click=”$ctrl.removeTask(t)” aria-label=”removebutton”>
</md-button>
It is important to note that this code is added within the ng-repeat div tag. Each element within the ITask array will need a remove button.
class=”md-fab md-mini” are just styling of the button
ng-click=”$ctrl.removeTask(t)” will call the removeTask function we will create in our taskController when the button is clicked
aria-label is needed due to making websites accessibly friendly or a error will apear in console log
Step 2: Creating function removeTask
Inside of task.html we passed in t which is an element in the ITask array. We want to thus find the index of that element in the array to spliace it
public removeTask = (task: Service.ITask) => {
var index = this.tasks.indexOf(task); //finds index of task we passed in
this.mainService.tasks.splice(index, 1); //splices that element from the array
}
A better way to remove the Task would be to remove it inside of AngularTemplate.Service
taskController.ts would then update to the following code passing in the index to remove task
Alternatively you can just pass in the task to mainService removeTask as well
public removeTask = (task: Service.ITask) => {
var index = this.tasks.indexOf(task);
this.mainService.removeTask(index);
}
mainCompSerice would then add in the following function to export class MainService
public removeTask = (index: number) => {
this.tasks.splice(index, 1);
}
Since, we did a lot of the work connecting the controller to the service already we now should be able to remove tasks.
Step 3: Adding in a trash can to remove button
<i class=”glyphicon glyphicon-trash”></i>
Adding the above code between the md-button tags will add a picture of a trash can to our button
Your code in task.html will look similar to the following:
Tasks:
<input type=”text” ng-model=”$ctrl.newTask”>
<md-button class=”md-raised” ng-click=”$ctrl.addTask()”> Add Task </md-button>
<div ng-repeat=”t in $ctrl.tasks track by t.id”>
{{$index}}
{{ t.name }}
<md-button class=”md-fab md-mini”
ng-click=”$ctrl.removeTask(t)” aria-label=”removebutton”>
<i class=”glyphicon glyphicon-trash”></i>
</md-button>
</div>
You will also need to add the libraries for glyphicon inside of index.html
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link rel=”stylesheet” href=”http://www.w3schools.com/lib/w3.css”>
<link rel=”stylesheet” href=”http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css”>
Phase 4: Summary
Step 1: Add summary-component tags to mainComp.html
Ensure that you are using the summaryComponent tags inside of mainComp.html or you will not see any of your results
You need to call summary.html in order to see summary.html code
Step 2: Make sure cummaryController has access to tasks in mainService
Inside of summaryController.ts we want to be able to acess our tasks from mainService
Add in our get tasks function
public get tasks() {
return this.mainService.tasks;
}
Step 3: Task length
We can now add some code into summary
{{ $ctrl.tasks.length }}
The above code will give us the length of tasks in mainService because we have a get task function inside of summary controller.
Step 4: Count number of tasks deleted
We would need to add in a counter to mainCompService.ts since we are storing all of our variables here and accessing them in the controller
Inside of export class MainService we added in a removeTask function
Inside this function we will want to increment our deletedCounter
public deletedCounter = 0; //creates the variable deletedCounter
public removeTask = (index: number) => {
this.tasks.splice(index, 1);
this.deletedCounter++;
}
summary.ts
public get deletedCounter() {
return this.mainService.deletedCounter;
}
Step 5: Calling deleteedCounter
Now inside of summary.html you can called the function deletedCounter to get access to mainService counter
<br> //this is just a line break to seperate the terms
length: {{ $ctrl.tasks.length }}
<br>
deleted Counter: {{ $ctrl.deletedCounter }}
Phase 5: Restricting delete until confirmed
Before we delete a task we want the user to confirm that the task is ready to be deleted. We want to restrict the user from using
the delete button until they press the check box to confirm the item for deletion
Step 1: Creating a field to track if the item has been confirmed for deletion
Inside of mainCompService.ts we will add another variable to our export interface ITask to hold information
on if the task has been confirmed or not
export interface ITask {
id: number;
name: string;
confirm: boolean;
}
Boolean is use to hold true false values. In this case we want to see if confirm is true or false.
Step 2: Update add task
Now that ITask has more than 2 variables add task will need to be updated setting confirm to default as false
public addTask = (newTask) => {
this.tasks.push({ id: this.idCounter, name: newTask, confirm: false});
this.idCounter++;
}
The item should not start off as True since the user just created the item it is unlikely they want it confirmed for deletion already
Step 3: Creating a check box
Now inside of task.html we will add in a input type check box
Since each task will need a check box we will add the checkbox within ng-repeat
<div ng-repeat=”t in $ctrl.tasks track by t.id”>
<input type=”checkbox” ng-model=”t.confirm” aria-label=”Toggle ngShow”>
{{$index}}
{{ t.name }}
<md-button class=”md-fab md-mini”
ng-click=”$ctrl.removeTask(t)” aria-label=”removebutton”>
<i class=”glyphicon glyphicon-trash”></i>
</md-button>
</div>
The type of input can change but for this purpose we want a true/false check box. ng-model is to indicate where the data of the check box will be stored
Again we will use the variable t that we defined in ng-repeat to be the elements in tasks tracked by id.
Lastly, aria-label is needed to avoid errors due to not being accessiblity friendly.
Step 4: Stopping people from deleting items if task not confirmed
Inside of our md-button we will add in a condition to stop users from deleting a item if it is not confirmed
ng-disabled=”!t.confirm”
ng-disable will disable the button is confirm is false. It will no longer be disabled if confirm is true.
<md-button class=”md-fab md-mini” ng-disabled=”!t.confirm”
ng-click=”$ctrl.removeTask(t)” aria-label=”removebutton”>
<i class=”glyphicon glyphicon-trash”></i>
</md-button>
Remember it is t.confirm because t is the task and confirm is the variable we need to access.
———————————————————-
Great work! Now you can try to implment some creative features of your own 🙂
Idea: Delete all, Edit, Keep a list of deleted tasks