One place for hosting & domains

      Angular

      Angular File Uploads with an Express Backend

      Introduction

      In this tutorial, I will be teaching how to upload files in Angular 2+.

      Throughout this tutorial, Angular means Angular version greater than 2.x unless stated otherwise.

      In this tutorial, I will also help you all create a server script that handles the file uploads.

      I will teach two methods of file uploads using Angular.

      The first method entails using the ng2-file-upload package, while the second method is handling the uploads without using any third-party package.

      This is what the app will look like when we are done building.

      For the server-side, we will be using Node.js (Express) for the script that handles the upload.

      To get started, we will need to install express generator, to take care of the configurations, and make our work much easier.

      So we run this command.

      1. sudo npm install -g express-generator

      Once Express generator has been installed, it’s time to create our application.

      So we run the following command.

      1. express -e angular-file

      After creating the application, we would need to move into the directory, and run npm install

      1. cd angular-file
      2. npm install

      At this point, if we run ng start command, we should be able to see the default Express page.

      The next step would be to install Multer. Multer is a package for handling file uploads in Express. To install Mutler, we run the following command.

      1. npm install multer --save

      At this point, we have multer installed, we would need to use it in the route handling our upload function.

      Let us open up our routes/index.js and replace it with the following:

      
      var express = require('express');
      
      var router = express.Router();
      
      var multer = require('multer');
      
      var DIR = './uploads/';
      
      var upload = multer({dest: DIR}).single('photo');
      
      
      router.get('/', function(req, res, next) {
      
        res.render('index', { title: 'Express' });
      });
      
      
      router.post('/', function (req, res, next) {
           var path = '';
           upload(req, res, function (err) {
              if (err) {
                
                console.log(err);
                return res.status(422).send("an Error occured")
              }
             
              path = req.file.path;
              return res.send("Upload Completed for "+path);
        });
      })
      module.exports = router;
      

      In the above route file, we imported the mutler library, Created a variable DIR, that holds the destination point, we then define an upload variable, that holds the mutler upload function telling it that it would be uploading a single file, with the name photo.

      In our post route, we call the upload function, acting as a middleware, adding a callback, so we can know if the file was uploaded or not.

      Once the file has been uploaded, mutler provides an interface for us to get the location of the file that has been uploaded, using the req.file.path, which we assign to a variable, and return it as the success message.

      At this point, however, if we try to access the route from any client and upload files to it, it would give a cors origin blocked error, which of cause, is right, as we are going to be calling the upload API from another domain.

      However, there’s a fix for that.

      Locate your app.js file in the root folder, and lets locate the line that says app.use("https://www.digitalocean.com/", routes) and just before that line, lets add the following:

      
      app.use(function(req, res, next) {
      
          res.header("Access-Control-Allow-Origin", "*");
          res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
          res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
          next();
      });
      

      What we have done above, is to create a middleware, that adds the cors origin header to the response, as well as allowed methods for that origin.

      At this point, we can hit CTRL+C on the terminal, and then run npm start again to reload changes.

      Our server script is now ready to receive and upload files to our root upload folder.

      Now it’s time we move to create the Angular project that does the file upload.

      Let’s move into the public folder, and we would use the angular-cli to create a new Angular project

      So we run the below command to install the angular-cli which is a Command Line Interface for developing Angular apps.

      So in our terminal, we run the following command to install it and create a new project.

      1. npm install -g angular-cli

      Change directory to the public folder of our working directory

      1. cd public

      Create a new angular project called testupload.

      1. ng new testupload

      Change directory to the test upload folder.

      1. cd testupload

      Serve the angular application.

      1. ng serve

      At this point, we see that npm packages are being installed, and as such, we wait till they are done.

      Once they have been installed, we can run the ng serve to serve our application for us to see.

      Extra Reading on Angular CLI – Use the Angular CLI For Faster Angular 2 Projects.

      Now it’s time to create the actual file upload.

      Method 1. Using the ng2-file-upload package.

      Now after we have served the application, we would see a screen like this when we navigate to localhost:4200.

      Now, let’s run the following command to install the ng2-file-upload package.

      1. npm i ng2-file-upload --save

      This installs the module into our node-modules folder, and as well saves it into our JSON file.

      Now let’s head over to the file in src/app/app.component.ts

      We would replace the contents of the file with this one below.

      
      import { Component, OnInit } from '@angular/core';
      
      import {  FileUploader } from 'ng2-file-upload/ng2-file-upload';
      
      const URL = 'http://localhost:8000/api/upload';
      
      @Component({
          
          selector: 'app-root',
          
          templateUrl: './app.component.html',
          styleUrls: ['./app.component.css']
      })
      export class AppComponent implements OnInit {
          
          
          public uploader:FileUploader = new FileUploader({url: URL, itemAlias: 'photo'});
          
          title = 'app works!';
      
          ngOnInit() {
              
              
              this.uploader.onAfterAddingFile = (file)=> { file.withCredentials = false; };
              
              
              this.uploader.onCompleteItem = (item:any, response:any, status:any, headers:any) => {
                  console.log("ImageUpload:uploaded:", item, status, response);
              };
          }
      }
      

      Here, we import component, alongside the OnInit class, so we can implement the ngOnInit function, which serve like a type of constructor for our component.

      Then we import the FileUploader class.

      We then define a constant that holds the URL we are uploading to.

      In our AppComponent class, we define a public property called uploader, and assign it to an instance of the file uploader, passing along our URL and an extra itemAlias property which we would call the File Input.

      The itemAlias property refers to the name we would like to call out file input.

      Overiding The onAfterAddingFile Function

      We then call the ngOnit function, where we override two of the uploaders function.

      The first function we override is the onAfterAddingFile function which is triggered after a file has been chosen, and we set the credentials to the file to be false. i.e., we are not authenticating with credentials.

      The next function we override is the onCompleteItem function. The reason we override this is so we can get the response from the server.

      In our case, we just console log the status, response, and the item.

      Now we move into our HTML file and replace it with the following content, so we can add the input type.

      <h1>
      
        {{title}}
      </h1>
      
      
      <input type="file" name="photo" ng2FileSelect [uploader]="uploader" />
      
      <button type="button" class="btn btn-success btn-s"
            (click)="uploader.uploadAll()"
            [disabled]="!uploader.getNotUploadedItems().length">
            Upload with ng-2 file uploader
      </button>
      

      So we create an input of type file and we attach the ng2FileSelect directive to it, which makes it possible to bind the uploader attribute which it provides to our own uploader.

      Then we create a button that is disabled if there is no item in the upload queue and has a click function to upload all files in the queue.

      However, if we save our file at this time and run it, we would run into errors.

      Including The Ng2FileSelect Directive

      We would have to add a declaration to our app.module.ts, so we can use the ng2FileSelect directive.

      So we add this line to the top of our app.module.ts:

      import { FileSelectDirective } from 'ng2-file-upload';
      

      And we also add the FileSelectDirective to our declarations

      declarations: [
          AppComponent,
          FileSelectDirective
        ],
      

      So our app.module.ts should look this way.

      import { BrowserModule } from '@angular/platform-browser';
      import { NgModule } from '@angular/core';
      import { FormsModule } from '@angular/forms';
      import { HttpModule } from '@angular/http';
      import { FileSelectDirective } from 'ng2-file-upload';
      import { AppComponent } from './app.component';
      
      @NgModule({
          declarations: [
              AppComponent,
              FileSelectDirective
          ],
          imports: [
              BrowserModule,
              FormsModule,
              HttpModule
          ],
        providers: [],
        bootstrap: [AppComponent]
      })
      export class AppModule { }
      

      Now if we launch our application and upload, we should see that our file is sent to the server.

      So on the Root folder, if we go to uploads folder, we should see that our images are being uploaded. Voilà we just uploaded files using Angular.

      However, there is a second method if we do not want to use the above plugin which requires using the underlying form-data to create the form data.

      Let us create an extra file input and button in our app.component.html,

      So our HTML structure looks this way.

      <h1>
      
        {{title}}
      </h1>
      
      
      <input type="file" name="photo" ng2FileSelect [uploader]="uploader" />
      
      <button type="button" class="btn btn-success btn-s"
          (click)="uploader.uploadAll()"
          [disabled]="!uploader.getNotUploadedItems().length">
          Upload with ng-2 file uploader
      </button>
      
      
      <input id="photo" type="file" />
      
      <button type="button" class="btn btn-success btn-s" (click)="upload()">
      Upload with method 2
      </button>
      

      Note that I have added another file input with an id of photo, and another button that has a click event to upload.

      Now let’s create the upload function that handles the file upload.

      Copy and replace your app component.ts with this.

      
      import { Component, OnInit, ElementRef, Input } from '@angular/core';
      
      import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
      
      import { Http, Response } from '@angular/http';
      
      import "rxjs/add/operator/do";
      
      import "rxjs/add/operator/map";
      const URL = 'http://localhost:8000/api/upload';
      
      
      @Component({
          
          selector: 'app-root',
          
          templateUrl: './app.component.html',
          styleUrls: ['./app.component.css']
      })
      export class AppComponent implements OnInit {
          
          
          public uploader:FileUploader = new FileUploader({url: URL, itemAlias: 'photo'});
          
          title = 'app works!';
      
          ngOnInit() {
            
            
            this.uploader.onAfterAddingFile = (file)=> { file.withCredentials = false; };
            
            
            this.uploader.onCompleteItem = (item:any, response:any, status:any, headers:any) => {
              console.log("ImageUpload:uploaded:", item, status, response);
            };
          }
          
          
          constructor(private http: Http, private el: ElementRef) {
      
          }
          
          upload() {
            
            let inputEl: HTMLInputElement = this.el.nativeElement.querySelector('#photo');
            
            let fileCount: number = inputEl.files.length;
            
            let formData = new FormData();
            
            if (fileCount > 0) { 
              
              formData.append('photo', inputEl.files.item(0));
              
              this.http
              
              
                .post(URL, formData).map((res:Response) => res.json()).subscribe(
                  
                  (success) => {
                    alert(success._body);
                  },
                  (error) => alert(error)
                )
            }
          }
      }
      

      What has changed?

      In this updated version, I have imported ElementRef and Input from @angular/core.

      I also imported the HTTP and response from the Angular HTTP library.

      I also went ahead to import the map and do rx’s functions to be used with our HTTP class.

      In the app component class, two things were added.

      1.) A constructor
      2.) The upload function.

      In the constructor, we pass in our HTTP and element ref instances, so they can be accessed by this.http and this.el.

      The upload function here is where the work lies.

      We declare inputel, which is of type htmlinputelement, and we set it to the instance of the file input we created with an id of photo using the nativelement.queryselector of the el.

      We then declare a variable filecount of type number and set it to the length of the files in the inputelement.

      We then use an if statement to be sure that a file was selected.

      We then loop through the file and we append the first element of the file as the value of the key ‘photo’ which our server expects and then append to our form data.

      We then call our HTTP library to post to our previously defined URL, sending the formData as params.

      At this point, if we try out our app and check the uploads folder, we should also see that the files are being uploaded.

      If you completed the above tutorial successfully, you have learned how to upload a file in Angular.

      We have seen two different methods of uploading files. For those who do not like using third-party libraries, we have used the underlying form data, and for those who do not mind using plugins, we have used the ng2-file-upload plugin by Valor.

      Create a Laravel and Angular Single Page Comment Application

      Introduction

      Laravel and Angular have both become very well renowned tools in the web development world lately. Laravel for the great things it brings to the PHP community and Angular for the amazing frontend tools and its simplicity. Combining these two great frameworks only seems like the logical next step.

      For our use cases, we will be using Laravel as the RESTful API backend and Angular as the frontend to create a very simple single-page comment application.

      This will be a simple example to show off how to get started using these two technologies so don’t hope for any extra database stuff on how to handle sub-comments or anything like that.

      This will be a simple single-page comment application:

      • RESTful Laravel API to handle getting, creating, and deleting comments
      • Angular frontend to handle showing our creation form and the comments
      • Ability to create a comment and see it added to our list without page refresh
      • Ability to delete a comment and see it removed from our list without page refresh

      Overall, these are very simple concepts. Our focus will be to see the intricacies of how Laravel and Angular can work together.

      Setting Up Laravel

      Go ahead and get your Laravel setup ready. We’ll be doing some basic things to get our backend to do CRUD on comments:

      • Create a database migration
      • Seed our database with sample comments
      • Create our routes for our API
      • Creating a catch-all route to let Angular handle routing
      • Creating a resource controller for comments

      Getting our Database Ready Migrations

      We will need a simple structure for our comments. We just need text and author. Let’s create our Laravel migration to create our comments.

      Let’s run the artisan command that will create our comments migration so that we can create the table in our database:

      1. php artisan migrate:make create_comments_table --create=comments

      We’ll use the Laravel Schema Builder to create the text and author fields that we need. Laravel will also create the id column and the timestamps so that we know how long ago the comment was made. Here is the code for the comments table:

      app/database/migrations/####_##_##_######_create_comments_table.php

          ...
      
              
              public function up()
              {
                  Schema::create('comments', function(Blueprint $table)
                  {
                      $table->increments('id');
      
                      $table->string('text');
                      $table->string('author');
      
                      $table->timestamps();
                  });
              }
      
          ...
      

      Make sure you go adjust your database settings in app/config/database.php with the right credentials. Now we will run the migration so that we create this table with the columns that we need:

      1. php artisan migrate

      With our table made, let’s create an Eloquent model so that we can interact with it.

      We will be using Laravel Eloquent models to interact with our database. This will be very easy to do. Let’s create a model: app/models/Comment.php.

      app/models/Comment.php

          <?php
      
          class Comment extends Eloquent { 
      

      We now have our new table and model. Let’s fill it with some sample data using Laravel Seeding.

      Seeding Our Database

      We will need a few comments so that we can test a few things. Let’s create a seed file and fill our database with 3 sample comments.

      Create a file: app/database/seeds/CommentTableSeeder.php and fill it with this code.

      app/database/seeds/CommentTableSeeder.php

          <?php
      
          class CommentTableSeeder extends Seeder {
      
              public function run()
              {
                  DB::table('comments')->delete();
      
                  Comment::create(array(
                      'author' => 'Chris Sevilleja',
                      'text' => 'Comment by Chris.'
                  ));
      
                  Comment::create(array(
                      'author' => 'Nick Cerminara',
                      'text' => 'Comment by Nick.'
                  ));
      
                  Comment::create(array(
                      'author' => 'Holly Lloyd',
                      'text' => 'Comment by Holly.'
                  ));
              }
      
          }
      

      To call this Seeder file, let’s open app/database/seeds/DatabaseSeeder.php and add the following:

      app/database/seeds/DatabaseSeeder.php

          ...
      
              
              public function run()
              {
                  Eloquent::unguard();
      
                  $this->call('CommentTableSeeder');
                  $this->command->info('Comment table seeded.');
              }
      
          ...
      

      Now let’s run our seeders using artisan.

      1. php artisan db:seed

      Now we have a database with a comment table, an Eloquent model, and samples in our database. Not bad for a day’s work… but we’re not even close to done yet.

      We will use Laravel’s resource controllers to handle our API functions for comments. Since we’ll be using Angular to display a resource and show create and update forms, we’ll create a resource controller with artisan without the create or edit functions.

      Let’s create our controller using artisan.

      1. php artisan controller:make CommentController --only=index,store,destroy

      For our demo app, we’ll only be using these three functions in our resource controller. To expand on this you’d want to include all the functions like update, show, update for a more fully-fledged app.

      Now we’ve created our controller. We don’t need the create and edit functions because Angular will be handling showing those forms, not Laravel. Laravel is just responsible for sending data back to our frontend. We also took out the update function for this demo just because we want to keep things simple. We’ll handle creating, showing, and deleting comments.

      To send data back, we will want to send all our data back as JSON. Let’s go through our newly created controller and fill out our functions accordingly.

      app/controllers/CommentController.php

          <?php
      
          class CommentController extends BaseController {
      
              
              public function index()
              {
                  return Response::json(Comment::get());
              }
      
              
              public function store()
              {
                  Comment::create(array(
                      'author' => Input::get('author'),
                      'text' => Input::get('text')
                  ));
      
                  return Response::json(array('success' => true));
              }
      
              
              public function destroy($id)
              {
                  Comment::destroy($id);
      
                  return Response::json(array('success' => true));
              }
      
          }
      

      You can see how easy it is to handle CRUD with Laravel and Eloquent. It’s incredibly simple to handle all the functions that we need.

      With our controller ready to go, the last thing we need to do for our backend is routing.

      Extra Reading: Simple Laravel CRUD with Resource Controllers

      Our Routes app/routes.php

      With our database ready to rock and roll, let’s handle the routes of our Laravel application. We will need routes to send users to the Angular frontend since that will have its own routing. We will also need routes for our backend API so people can access our comment data.

      Let’s create the Angular pointing routes. We will need one for the home page and a catch-all route to send users to Angular. This ensures that any way a user accesses our site, they will be routed to the Angular frontend.

      We’ll be prefixing our API routes with… (drumroll please)… api. This way, if somebody wants to get all comments, they will use the URL: http://example.com/api/comments. This just makes sense moving forward and is some basic API creation good tactics.

      app/routes.php

          <?php
          
          
          
          
          Route::get('/', function() {
              View::make('index'); 
          });
      
          
          Route::group(array('prefix' => 'api'), function() {
      
              
              
              
              Route::resource('comments', 'CommentController',
                  array('only' => array('index', 'store', 'destroy')));
      
          });
      
          
          
          
          App::missing(function($exception) {
              return View::make('index');
          });
      

      We now have our routes to handle the 3 main things our Laravel backend needs to do.

      Handling Catch-All Routes: In Laravel, you can do this a few ways. Usually, it isn’t ideal to do the above code and have a catch-all for your entire application. The alternative is that you can use Laravel Controller Missing Methods to catch routes.

      Testing All Our Routes Let’s make sure we have all the routes we need. We’ll use artisan and see all our routes:

      1. php artisan routes

      This command will let us see our routes and sort of a top-down view of our application.

      We can see the HTTP verb and the route used to get all comments, get a single comment, create a comment, and destroy a comment. On top of those API routes, we can also see how a user gets routed to our Angular application by the home page route.

      Backend Done

      Finally! Our Laravel API backend is done. We have done so much and yet, there’s still so much to do. We have set up our database and seeded it, created our models and controllers, and created our routes. Let’s move on to the frontend Angular work.

      I’ve seen this question asked a lot. Where exactly should I be putting Angular files and how does Laravel and Angular work together. We did an article on Using Laravel Blade with AngularJS. This article works under the assumption that we aren’t even going to use Blade.

      To let Angular handle the frontend, we will need Laravel to pass our user to our index.php file. We can place this in a few different places. By default, when you use:

      app/routes.php

          Route::get('/', function() {
              return View::make('index');
          });
      

      This will return app/views/index.php. Laravel will by default look in the app/views folder.

      Some people may want to keep Angular files completely separate from Laravel files. They will want their entire application to be housed inside of the public folder. To do this is simple: just change the default View location to the public folder. This can be done in the app/config/view.php file.

      app/config/view.php

          ...
      
              
              'paths' => array(__DIR__.'/../../public/views'),
      
          ...
      

      Now return View::make('index') will look for public/views/index.php. It is all preference on how you’d like to structure your app. Some people see it as a benefit to have the entire Angular application in the public folder so that it is easier to handle routing and if it is needed in the future, to completely separate the backend RESTful API and the Angular frontend.

      For Angular routing, then your partial files will be placed in the public folder, but that’s out of the scope of this article. For more information on that kind of single-page Angular routing, check out Single Page Angular Application Routing.

      Let’s assume we left everything default and our main view file is in our app/views folder and move forward.

      Routing with Laravel and Angular There are a lot of questions about having routing with Laravel and Angular and if they conflict. Laravel will handle the main routing for your application. Angular routing will only happen when Laravel routes our user to the main Angular route (index.php) in this case. This is why we use a Laravel catch-all route. Laravel will handle the API routes and anything it doesn’t know how to route will be sent to Angular. You can then set up all the routing for your Angular application to handle showing different views.

      Getting Our Application Ready

      Everything for our Angular application will be handled in the public folder. This lets us keep a good separation of the backend in the app folder.

      Let’s look at the application structure we will have in our public folder. We’ve created our Angular application to be modular since that is best practice. Now our separated parts of our application will be easy to test and work with.

          - public/
          ----- js/
          ---------- controllers/ // where we will put our angular controllers
          --------------- mainCtrl.js
          ---------- services/ // angular services
          --------------- commentService.js
          ---------- app.js
      

      Our Angular service is going to be the primary place where we will have our HTTP calls to the Laravel API. It is pretty straightforward and we use the Angular $http service.

      public/js/services/commentService.js

          angular.module('commentService', [])
      
          .factory('Comment', function($http) {
      
              return {
                  
                  get : function() {
                      return $http.get('/api/comments');
                  },
      
                  
                  save : function(commentData) {
                      return $http({
                          method: 'POST',
                          url: '/api/comments',
                          headers: { 'Content-Type' : 'application/x-www-form-urlencoded' },
                          data: $.param(commentData)
                      });
                  },
      
                  
                  destroy : function(id) {
                      return $http.delete('/api/comments/' + id);
                  }
              }
      
          });
      

      This is our Angular service with 3 different functions. These are the only functions we need since they will correspond to the API routes we made in our Laravel routes.

      We will be returning the promise object from our service. These will be dealt with in our controllers. The naming convention here also stays the same as the Laravel controller that we have.

      With our Angular Service done, let’s go into our controller and use it.

      Angular Controller public/js/controllers/mainCtrl.js

      The controller is where we will have most of the functionality for our application. This is where we will create functions to handle the submit forms and deleting on our view.

      public/js/controllers/mainCtrl.js

          angular.module('mainCtrl', [])
      
          
          .controller('mainController', function($scope, $http, Comment) {
              
              $scope.commentData = {};
      
              
              $scope.loading = true;
      
              
              
              
              Comment.get()
                  .success(function(data) {
                      $scope.comments = data;
                      $scope.loading = false;
                  });
      
              
              
              $scope.submitComment = function() {
                  $scope.loading = true;
      
                  
                  
                  Comment.save($scope.commentData)
                      .success(function(data) {
      
                          
                          Comment.get()
                              .success(function(getData) {
                                  $scope.comments = getData;
                                  $scope.loading = false;
                              });
      
                      })
                      .error(function(data) {
                          console.log(data);
                      });
              };
      
              
              
              $scope.deleteComment = function(id) {
                  $scope.loading = true;
      
                  
                  Comment.destroy(id)
                      .success(function(data) {
      
                          
                          Comment.get()
                              .success(function(getData) {
                                  $scope.comments = getData;
                                  $scope.loading = false;
                              });
      
                      });
              };
      
          });
      

      As you can see in our controller, we have injected our Comment service and use it for the main functions: get, save, and delete. Using a service like this helps to not pollute our controller with $http gets and puts.

      Connecting Our Application public/js/app.js

      On the Angular side of things, we have created our service and our controller. Now let’s link everything together so that we can apply it to our application using ng-app and ng-controller.

      This will be the code to create our Angular application. We will inject the service and controller into. This is a best practice since it keeps our application modular and each different part can be testable and extendable.

      public/js/app.js

          var commentApp = angular.module('commentApp', ['mainCtrl', 'commentService']);
      

      That’s it! Not much to it. Now we’ll actually get to our view where we can see how all these Angular parts work together.

      Our Main View app/views/index.php

      So far, after everything we’ve done up to this point, we still won’t be able to see anything in our browser. We will need to define our view file since Laravel in our home route and our catch-all route returns return View::make('index');.

      Let’s go ahead and create that view now. We will be using all the Angular parts that we’ve created. The main parts that we’ve created from Angular that we’ll use in index.php are:

      • ng-app and ng-controller: We’ll apply these to our application by attaching them to our body tag
      • ng-repeat: We’ll loop over the comments and display them in our template
      • submitComment(): We’ll attach this function to our form using ng-submit
      • Loading Icons: We’ll create a variable called loading. If it is set to true, we’ll show a loading icon and hide the comments
      • deleteComment(): We’ll attach this function to a delete link so that we can remove the comment

      Now let’s get to the actual code for our view. We’ll comment out the main important parts so we can see how everything works together.

      app/views/index.php

          <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Laravel and Angular Comment System</title>
      
              <!-- CSS -->
              <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css"> <!-- load bootstrap via cdn -->
              <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
              <style>
                  body        { padding-top:30px; }
                  form        { padding-bottom:20px; }
                  .comment    { padding-bottom:20px; }
              </style>
      
              <!-- JS -->
              <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
              <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script> <!-- load angular -->
      
              <!-- ANGULAR -->
              <!-- all angular resources will be loaded from the /public folder -->
                  <script src="js/controllers/mainCtrl.js"></script> <!-- load our controller -->
                  <script src="js/services/commentService.js"></script> <!-- load our service -->
                  <script src="js/app.js"></script> <!-- load our application -->
      
      
          </head>
          <!-- declare our angular app and controller -->
          <body class="container" ng-app="commentApp" ng-controller="mainController"> <div class="col-md-8 col-md-offset-2">
      
              <!-- PAGE TITLE =============================================== -->
              <div class="page-header">
                  <h2>Laravel and Angular Single Page Application</h2>
                  <h4>Commenting System</h4>
              </div>
      
              <!-- NEW COMMENT FORM =============================================== -->
              <form ng-submit="submitComment()"> <!-- ng-submit will disable the default form action and use our function -->
      
                  <!-- AUTHOR -->
                  <div class="form-group">
                      <input type="text" class="form-control input-sm" name="author" ng-model="commentData.author" placeholder="Name">
                  </div>
      
                  <!-- COMMENT TEXT -->
                  <div class="form-group">
                      <input type="text" class="form-control input-lg" name="comment" ng-model="commentData.text" placeholder="Say what you have to say">
                  </div>
      
                  <!-- SUBMIT BUTTON -->
                  <div class="form-group text-right">
                      <button type="submit" class="btn btn-primary btn-lg">Submit</button>
                  </div>
              </form>
      
              <!-- LOADING ICON =============================================== -->
              <!-- show loading icon if the loading variable is set to true -->
              <p class="text-center" ng-show="loading"><span class="fa fa-meh-o fa-5x fa-spin"></span></p>
      
              <!-- THE COMMENTS =============================================== -->
              <!-- hide these comments if the loading variable is true -->
              <div class="comment" ng-hide="loading" ng-repeat="comment in comments">
                  <h3>Comment 
                  <p>{{ comment.text }}</p>
      
                  <p><a href="#" ng-click="deleteComment(comment.id)" class="text-muted">Delete</a></p>
              </div>
      
          </div>
          </body>
          </html>
      

      Now we finally have our view that brings all of the parts we created together. You can go ahead and play around with the application. All the parts should fit together nicely and creating and deleting comments should be done without a page refresh.

      Make sure you take a look at the GitHub repo to test the application. Here are some quick instructions to get you going.

      1. Clone the repo: git clone [email protected]:scotch-io/laravel-angular-comment-app
      2. Install Laravel: composer install --prefer-dist
      3. Change your database settings in app/config/database.php
      4. Migrate your database: php artisan migrate
      5. Seed your database: php artisan db:seed
      6. View your application in the browser!

      Hopefully, this tutorial gives a good overview of how to start an application using Laravel and Angular. You can bring this further and create a full application that can handle multiple API calls on the Laravel side, and even create your own Angular routing for multiple pages.

      Sound off in the comments if you have any questions or would like to see a specific use case. We can also expand on this demo and start adding different things like editing a comment, user profiles, whatever.

      3 Useful TypeScript Tips for Angular

      Introduction

      These are the 3 tips I found pretty handy while working with TypeScript:

      1. Eliminating the need to import interfaces
      2. Making all interface properties optional
      3. Stop throwing me an error, I know what I’m doing

      Though I discovered these while working with Angular applications, all tips are not Angular-specific, it’s just TypeScript.

      I like interfaces. However, I don’t like to import them every time. Although Visual Studio Code has an auto-import feature, I don’t like my source files been “polluted” by multiple lines of imports – just for the purpose of strong typing.

      This is how we do it normally.

      
      export interface Customer {
        id: number;
        name: string;
      }
      
      export interface User {
        id: number;
        isActive: boolean;
      }
      
      
      import { Customer, User } from './api.model'; 
      
      export class MyComponent {
        cust: Customer;
      }
      

      Solution 1: Using namespace

      By using namespace, we can eliminate the need to import interfaces files.

      
      namespace ApiModel {
        export interface Customer {
          id: number;
          name: string;
        }
      
        export interface User {
          id: number;
          isActive: boolean;
        }
      }
      
      
      export class MyComponent {
        cust: ApiModel.Customer;
      }
      

      Nice right? Using namespace also helps you to better organize and group the interfaces. Please note that you can split the namespace across many files.

      Let’s say you have another file called api.v2.model.ts. You add in new interfaces, but you want to use the same namespace.

      
      namespace ApiModel {
        export interface Order {
          id: number;
          total: number;
        }
      }
      

      You can definitely do so. To use the newly created interface, just use them as the previous example.

      
      export class MyComponent {
        cust: ApiModel.Customer;
        order: ApiModel.Order;
      }
      

      Here is the detail documentation on TypeScript namespacing.

      Solution 2: Using d file

      The other way to eliminate import is to create a TypeScript file end with .d.ts. “d” stands for declaration file in TypeScript (more explanation here).

      
      
      interface Customer {
        id: number;
        name: string;
      }
      

      Use it as normal without the need to import it.

      
      export class MyComponent {
        cust: Customer;
      }
      

      I recommend solution 1 over solution 2 because:

      • d file usually use for external, 3rd party declaration
      • namespace allows us to better organize the files

      It’s quite common where you will use the same interface for CRUD. Let’s say you have a customer interface, during creation, all fields are mandatory, but during an update, all fields are optional. Do you need to create two interfaces to handle this scenario?

      Here is the interface

      
      export interface Customer {
        id: number;
        name: string;
        age: number;
      }
      

      Solution: Use Partial

      Partial is a type to make properties an object optional. The declaration is included in the default d file lib.es5.d.ts.

      
      type Partial<T> = {
          [P in keyof T]?: T[P];
      };
      

      How can we use that? Look at the code below:

      
      import { Customer } from './api.model';
      
      export class MyComponent {
        cust: Partial<Customer>;  /
      
        ngOninit() {
          this.cust = { name: 'jane' }; 
        }
      }
      

      If you don’t find Partial declaration, you may create a d file yourself (e.g. util.d.ts) and copy the code above into it.

      For more advanced type usage of TypeScript, you can read here.

      As a JavaScript-turned-TypeScript developer, one might find TypeScript error is annoying sometimes. In some scenarios, you just want to tell TypeScript, “Hey, I know what I am doing, please leave me alone.”.

      From TypeScript version 2.6 onwards, you can do so by using comment @ts-ignore to suppress errors.

      For example, TypeScript will throw error “Unreachable code detected” in this following code:

      if (false) {
        console.log('x');
      }
      

      You can suppress that by using comment @ts-ignore

      if (false) {
        
        console.log('x');
      }
      

      Find out more details here: TypeScript 2.6 release

      Of course, I will suggest you always try to fix the error before ignoring it!

      TypeScript is good for your (code) health. It has pretty decent documentation. I like the fact that they have comprehensive What's new documentation for every release. It’s an open source project in GitHub if you would like to contribute. The longer I work with TypeScript, the more I love it and appreciate it.

      That’s it, happy coding!