📂
SEI 1019
  • Introduction
  • About These Notes
  • Syllabus
  • Development Workflow
    • Command Line
      • The Terminal
      • Filesystem Navigation
      • File Manipulation
      • Additional Topics
    • Intro to Git
      • Version Control
      • Local Git
      • Remote Git
      • Git Recipes
    • Group Collaboration
      • Git Workflows
      • Project Roles and Tools
    • VS Code Tips & Tricks
  • HTML/CSS
    • HTML
    • CSS Selectors
    • CSS Box Model and Positioning
      • Box Model
      • Display and Positioning
      • Flexbox
      • Grid
      • Flexbox & Grid Games
      • Floats and Clears
      • Additional Topics
    • Advanced CSS
      • Responsive Design
      • Pseudo-Classes/Elements
      • Vendor Prefixes
      • Custom Properties
      • Additional Topics
    • Bootstrap
    • CSS Frameworks
    • Accessibility
  • JavaScript
    • Primitives
    • Arrays
    • Objects
    • Control Flow
      • Boolean Expressions
      • Conditionals
      • Loops
      • Promises
    • Functions
      • Callbacks
      • Timing Functions
      • Iterators
    • DOM and Events
    • DOM Manipulation
    • HTML5 Canvas
    • How To Reduce Redundancy
    • (2019) JavaScript OOP
    • (2016) OOP with Classes
    • (1995) OOP with Prototypes
      • Constructors
      • Prototypes
    • Intro to TDD
    • Scoping
    • Inheritance
      • Prototypal Inheritance
      • Call, Apply, and other Functions
      • ES6 Inheritance
      • Resources
    • Custom Node Modules
    • Additional Topics
      • AJAX, Fetch, and Async/Await
      • AJAX w/JSON and Localstorage
        • AJAX w/JSON
        • Local Storage
      • Async module
      • Data Scraping
  • jQuery
    • Intro
      • DOM Manipulation
      • Reddit Practice
      • Styling
      • Events
    • Plugins
    • AJAX
  • APIs
    • Fetch
    • AJAX w/jQuery
    • AJAX w/Fetch
  • Databases
    • Intro to SQL
    • Advanced SQL
    • MongoDB
      • Intro to NoSQL
      • CRUD in MongoDB
      • Data Modeling
      • Intermediate Mongo
  • Node/Express
    • Node
      • Intro to Node
      • Node Modules
      • Node Package Manager (NPM)
    • Express
      • Intro to Express
        • Routes
        • Views
        • Templates
        • Layouts and Controllers
        • CRUD & REST
          • Get and Post
          • Put and Delete
      • APIs with Express (request)
      • APIs with Express (axios)
    • Sequelize
      • Terminology
      • Setup
      • Using Models
      • Seeding Data
      • Validations and Migrations
      • Resources
      • 1:M Relationships
      • N:M Relationships
    • Express Authentication
      • Research Components
      • Code Components
      • Auth in Theory
        • Sessions
        • Passwords
        • Middleware
        • Hooks
      • Auth in Practice
        • Create the User
        • User Signup
        • Sessions
        • User Login
        • Authorization and Flash messages
    • Testing with Mocha and Chai
    • Mongoose
      • Mongoose Associations
    • JSON Web Tokens
      • Codealong
    • Additional Topics
      • oAuth
      • Geocoding with Mapbox
      • Geocoding and Google Maps
      • Cloudinary
      • Websockets with Socket.io
      • SASS
  • Ruby
    • Intro to Ruby
    • Ruby Exercises
    • Ruby Classes
    • Ruby Testing with Rspec
    • Ruby Inheritance
    • Ruby Data Scraping
  • Ruby on Rails
    • Intro to Rails
    • APIs with Rails
    • Asset Pipeline
    • Rails Auth and 1-M
      • Auth Components
    • Rails N:M
    • ActiveRecord Polymorphism
    • Additional Topics
      • oAuth
      • SASS
      • Rails Mailers
      • Cloudinary
      • Jekyll
  • React (Updated 2019)
    • ES6+/ESNext
      • Const and Let
      • Arrow Functions
      • Object Literals and String Interpolation
      • ES6 Recap
      • ES6 Activity
    • Intro to React
      • Create React App
      • Components and JSX
      • Virtual DOM
      • Props
      • Dino Blog Activity
      • Nested Components
      • Lab: LotR
    • React State
      • Code-Along: Mood Points
      • Code-Along: Edit Dino Blog
      • Lab: Simple Calc
      • Lifting State
    • React Router
      • Browser History/SPAs
      • React Router (lesson and full codealong)
      • Router Lab
    • Fetch and APIs
      • APIs with Fetch and Axios
      • Fetch the Weather
    • React Hooks
    • React LifeCycle
      • Lab: Component LifeCycle
    • React Deployment
    • Additional Topics
      • React Frameworks
        • Material UI Theming
      • Typescript
        • More Types and Syntax
        • Tsconfig and Declaration Files
        • Generics with Linked List
      • Redux
      • TypeScript
      • Context API
      • React Native
  • Meteor
  • Deployment and Config
    • Installfest
      • Mac OSX
      • Linux
      • Git Configuration
      • Sublime Packages
    • Deploy - Github Pages
    • Deploy - Node/Sequelize
    • Deploy - Node/MongoDB
    • Deploy React
    • Deploy - Rails
      • Foreman (Environment Variables)
    • Deploy - AWS Elastic Beanstalk
    • Deploy - S3 Static Sites
    • Deploy - Django
    • Deploy - Flask
  • Data Structures and Algorithms
    • Recursion
    • Problem Solving - Array Flatten
    • Binary Search
    • Algorithm Complexity
    • Stacks and Queues
    • Bracket Matching
    • Ruby Linked Lists
      • Sample Code
      • Beginner Exercises
      • Advanced Exercises
    • JS Linked Lists
      • Sample Code
      • Beginner Exercises
      • Beginner Solutions
    • Hash Tables
    • Intro to Sorting
    • Insertion Sort
    • Bucket Sort
    • Bubble Sort
    • Merge Sort
    • Quick Sort
    • Heap Sort
    • Sorting Wrapup
    • Hashmaps
    • Trees and Other Topics
  • Python
    • Python Installation
    • Intro to Python
    • Python Lists
    • Python Loops
    • Python Dictionaries
    • Python Sets and Tuples
    • Python Cheatsheet
    • Python Functions
    • Python Classes
    • Python Class Inheritance
    • Intro to Flask
    • Intro to SQLAlchemy
      • Flask and SQLAlchemy
    • Using PyMongo
    • Intro to Django
    • CatCollector CodeAlong
      • URLs, Views, Templates
      • Models, Migrations
      • Model Form CRUD
      • One-to-Many Relations
      • Many-to-Many Relations
      • Django Auth
    • Django Cheatsheet
    • Django Auth
    • Django Polls App Tutorial
    • Django School Tool Tutorial
    • Django 1:M Relationships
    • Custom Admin Views
    • Data Structures and Algorithms
      • Recursion
      • Binary Search
      • Stacks and Queues
      • Linked Lists
      • Binary Trees
      • Bubble Sort
      • TensorFlow & Neural Networks
    • Adjacent Topics
      • Raspberry Pi
      • Scripting
  • Assorted Topics
    • History of Computer Science
    • Regular Expressions
    • Intro to WDI (Course Info)
    • Being Successful in WDI
    • Internet Fundamentals
      • Internet Lab
    • User Stories and Wireframing
      • Wireframing Exercise: Build an Idea
    • Post WDI
      • Learning Resources
      • Deliverables -> Portfolio
      • FAQ
  • Projects
    • Project 1
    • Project 2
    • Project 3
      • Project 3 Pitch Guidelines
    • Project 4
    • Past Projects
      • Project 1
      • Project 2
      • Project 3
      • Project 4
      • Portfolios
    • Post Project 2
    • MEAN Hackathon
      • Part 1: APIs
      • Part 2: Angular
    • Portfolio
  • Web Development Trends
  • Resources
    • APIs and Data
    • Tech Websites
    • PostgreSQL Cheat Sheet
    • Sequelize Cheat Sheet
    • Database Administration
  • Archived Section
    • (Archived) ReactJS
      • Intro to React
        • Todo List Codealong
        • Additional Topics
      • Deploy React
      • React with Gulp and Browserify
        • Setting up Gulp
        • Additional Gulp Tasks
      • React Router
        • OMDB Router
        • OMDB Search
        • Additional Resources
      • React Animations
        • CSS Animations
    • AngularJS
      • Intro to AngularJS
        • Components and SPA
        • Create an Angular App
      • Angular Directives and Filters
      • Angular Animation
      • Angular Bootstrap Directives
        • Bootstrap Modals
      • Angular $http
      • Angular Services
        • Service Recipes
        • ngResource
        • Star Wars Codealong
      • Angular Routing
      • Angular + Express
      • Angular Authentication
        • Additional Topics
      • Angular Components
      • Angular Custom Filters
      • Angular Custom Directives
Powered by GitBook
On this page
  • Objectives
  • Modularity with Custom Directives
  • Real World Example
  • Cards Against Assembly
  • Building a Simple Directive
  • Let's be organized!
  • Directives are as easy as...
  • Directive Options
  • Before we continue... additional topics
  • Conclusion

Was this helpful?

  1. Archived Section
  2. AngularJS

Angular Custom Directives

PreviousAngular Custom Filters

Last updated 4 years ago

Was this helpful?

Warning: This content is considered archived as .directive is no longer used - this changed between versions Angular 1.4 and 1.5. There is a somewhat updated version of the lab described here that can be found on . Both this project and this page are on our list to be refactored for Angular 1.5+.

Objectives

  • Describe the different types of custom directives available

  • Create a custom directive

  • Explain why custom directives are valuable

Modularity with Custom Directives

As you've seen by now, a huge amount of the code you work with in Angular are directives. Angular was designed to be an extension of HTML - a way to have custom-defined interactive tags of your own making.

While we've been getting good at using the directives that come with Angular, it's time to start seeing what we can do if we start making own own.

One of the most obvious uses of this is when you've got repetitive code to render some information or data. If you're using a bunch of tags all over the place, it's a DRY principle – you might forget a tag or decide to change something and have to change it in a million places.

By extracting it to a custom directive, we can reference that directive whenever we need to use it and not worry about repeating the code to render it.

Real World Example

As an example, we're going to mess around with duplicating something that's becoming a common pattern in interface design – the concept of a card. Applications like Twitter, Pinterest, Facebook, and a lot more are moving towards this design pattern.

Twitter

Let's see if we can make something similar, wrapped up in our own custom HTML element.

Cards Against Assembly

Starter Code

Take five minutes and inspect our starter code. You'll see a pretty normal Angular app, and since we're repeating using those cards, and there's a few consistent tags we're repeating every time we render a card, we're going to experiment with making those cards a custom-defined directive.

Building a Simple Directive

Using our starter code, our goal is to take:

<div class='card'>
  <h4 class="card-title">{{card.question}}</h4>
  <h6>Cards Against Assembly</h6>
</div>

and end up with a reusable <card></card> component, maybe something like:

<card question="{{card.question}}"></card>

Let's be organized!

Rather than just throw this wherever, let's make a file dedicated to this directive.

touch js/cardView.js

Directives are as easy as...

Just like controllers, factories, anything else we've made in angular, we're going to use a function to add another component. In this case, we'll make a cardView module and add a directive to it. That directive will have a function that returns everything related to the directive.

angular.module('cardView', [])
.directive('card', function() {
  return {
    //stuff here
  }
});

An important thing to point out: The first argument of directive is the name of the directive and how you'll use it in your HTML; and remember, Angular converts camelCase to snake-case for us, so if you want to use <secret-garden></secret-garden> in your HTML, name your directive .directive('secretGarden', ...). Remember, in the official Angular docs it's called ngClass or ngRepeat, but in your HTML you use ng-class and ng-repeat.

Directive Options

There's a few interesting options when making your own directives. We'll go through them all, quickly, and you can play with them on your own in a bit.

  1. restrict

  2. replace

  3. template/templateUrl

  4. scope

1. restrict

While the name isn't obvious, the restrict option lets us decide what kind of directive we want to make. It looks like this:

restrict: 'AEC'
  • E is element. An HTML element, like <card></card>

  • A is attribute. Like <div card="something"></div>

  • C is class. Like <div class="card"></div>

You can choose to have just one, all of the above, or any combination you like. You should steer towards elements & attributes as much as possible, though – classes can get messy with other CSS classes.

For ours, let's play with just an element.

angular.module('cardView', [])
.directive('card', function() {
  return {
    restrict: 'E'
  }
});

2. replace

Replace is pretty straightforward. Should this directive replace the HTML? Do you want it to get rid of what's in the template & swap it out with the template we're going to make? Or add to it, and not remove the original. For example, replacing would mean:

<div ng-repeat="card in cards">
  <card></card>
</div>

Would actually render as:

<div ng-repeat="card in cards">
  <div class='card'>
    <h4 class="card-title">{{question}}</h4>
    <h6>Cards Against Assembly</h6>
  </div>
</div>

See, replaced. Let's say we like that for our example:

angular.module('cardView', [])
.directive('card', function() {
  return {
    restrict: 'E',
    replace: true
  }
});

3. template/templateUrl

This is where our partial view comes in. Now, if it's a pretty tiny, self-contained directive, you can use template: "Some javascript " + string + " concatenation";

But that easily starts getting ugly, so it's often better (even for small directives like this) to make a quick little partial HTML file and reference it with templateUrl instead.

Let's extract our existing card tags, and throw them in a partial. Cut out:

<div class='card'>
  <h4 class="card-title">{{card.question}}</h4>
  <h6>Cards Against Assembly</h6>
</div>

Quickly touch cardView.html or some similarly obvious-named partial, and paste it back in.

In cards/cardView.html:

<div class='card'>
  <h4 class="card-title">{{card.question}}</h4>
  <h6>Cards Against Assembly</h6>
</div>

In js/cardView.js, we can add our option:

angular.module('cardView', [])
.directive('card', function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'cardView.html'
  }
});

And lastly, in our index.html, let's finally use our custom directive.

<!-- index.html -->
<div class='col-sm-6 col-md-6 col-lg-4' ng-repeat="card in cards">
  <card></card>
</div>

Before testing out the directive, make sure that index.html has the script tag for the directive, and the directive module is injected into the app. Also, remember to run this using a HTTP server

In app.js:

angular.module('CardsAgainstAssembly', ['CardCtrls', 'cardView']);

TRY IT! So awesome! We've now got this much more readable index.html, with a very semantic HTML tag describing exactly what we want rendered.

This is awesome. This is a great, reusable component. Except for one thing.

4. scope

If you notice, our template uses {{card.question}} inside it. But what if we wanted to render a card somewhere outside of that ng-repeat, where card in cards isn't a thing. What if we want to render a one-off card, reusing our new directive elsewhere?

Just like controllers, we want to define what our scope is. We want to be able to say "Render a card, with these details, in whatever context I need to render it in." A card shouldn't rely on a controller's data to know what information to render inside it. The controller should pass that data to our directive, so it's freestanding and not relying on anyone but itself.

That's where scope comes in, and this lets us decide what attributes our element should have! For example, in our card example, maybe we want to render a card with just a string somewhere outside of this controller. We want to make our own card with our own hardcoded text.

Try this. In your index.html, adjust our <card> element to say:

<card question="{{card.question}}"></card>

In context, you'll see that the ng-repeat is giving us the variable card, and we're actually just rendering that out as a string. But we've decided we want to have an attribute called question to pass data through. We made that up, it's appropriate to our example, but it can be anything.

There are only two other pieces we need to make this reality.

In our cardView.html partial, let's adjust to:

<div class='card'>
  <h4 class="card-title">{{question}}</h4>
  <h6>Cards Against Assembly</h6>
</div>

No longer reliant on a variable named card, it's now just reliant on an element having the attribute of question.

And finally, in js/cardView.js:

angular.module('cardView', [])
.directive('card', function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'cardView.html',
    scope: {
      question: '@'
    }
  }
});

In scope, we just define an object. The key is whatever want the attribute on the element to be named. So if we want <card bagel=""></card>, then we'd need a key named bagel in our scope object.

The Different Types of Scope for a Directive

The value is one of 3 options.

scope: {
  myRecipient: '=', // Bind the ngModel to the object given
  onSend: '&',      // Pass a reference to the method
  fromName: '@'     // Store the value associated by fromName
}

The corresponding options would look like:

<div scope-example my-recipient="to" on-send="sendMail(email)" from-name="ari@fullstack.io" />

The = is a mechanism for binding data that might change; the & is for passing a reference to a function you might want to call; and the @ is simply storing a string & passing it through to the template.

Since we've decided to use @/strings, let's try it!

Our last test is to see if we can make a card using just a hardcoded string. Then we'll know our card is really reusable.

Somewhere outside the context of the controller, let's say just above the footer in our index.html, throw a handmade card in:

<!-- ... -->
</section>
<hr>
<card question="Why is Angular so awesome?"></card>
<footer>
<!-- ... -->

Would you look at that? Our own custom directive - a reusable, semantic HTML component that we designed ourselves.

Before we continue... additional topics

Your assignment will involve making directives with more functionality than just a card. In order to provide additional functionality, the controller property is incredibly useful for creating a separate controller for the entire directive.

Example:

angular.module('cardView', [])
.directive('card', function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'cardView.html',
    scope: {
      question: '@'
    },
    controller: ['$scope', function($scope) {
      //we can access 'question' or anything from scope via $scope.question
      //we can also create functions that are accessible within the template
      $scope.printQuestion = function() {
        console.log($scope.question);
      }
    }]
  }
});

Now if we update cardView.html, we can add this function to the template. Let's do so via the ng-click directive.

<div class='card' ng-click="printQuestion()">
  <h4 class="card-title">{{question}}</h4>
  <h6>Cards Against Assembly</h6>
</div>

Conclusion

  • Where can you imagine using custom directives?

  • What four types of directives can you make?

  • How do you pass information into a custom directive?

Everyone's favorite CSS framework, Bootstrap, is even on board, where in with just some CSS classes:

Bootstrap

Cards Against Assembly
Cards Against Assembly
Custom Card
version 4+ you're able to create a card
https://github.com/WDI-SEA/cards-against-assembly
GitHub