AngularJS directive not showing up on template

18,833

Solution 1

During normalization, Angular converts - delimited name to camelCase.

So use camelCase while specifying the directive inside JS:

.directive('myDirective', function () {

Fiddle

Solution 2

I'm sure you've figured this out already, but if you change your scope definition for name to be

scope: {
  name: '@'
}

you will then be able to pass a string. The '@' interpolates the attribute while the '=' binds it. Additionally, you don't need to include an attribute name if it is the same as the scope variable.

Solution 3

The problem appears to be in the directive definition. You note in your question that Angular expects an object; this is true for the "=" scope, but not for the "@" scope. In the "@" scope, Angular expects a string only. I have created a snippet below.

Too many modules

Unless you are reusing the directive in multiple applications, do not create a new module for it. Add the directive definition to the module that you created for the application. In my example below, I called the module back by using "angular.module( moduleName )"... When only one argument is used, Angular returns the existing object rather than creating a new one. This is how we can separate the code into many files.

Things to Note

You will notice the following:

  1. You do not need to load the module into the app variable. Calling the Singleton each time is actually safer and easier on memory management.
  2. The directive is in camel case, as you have already noted.
  3. I am setting the name attribute to a string value and not an object; this works because of the "@" scope setting.
  4. The div is set to ng-app='MyApp'. This usually is set to the html element, but I did not want to mess with the DOM on Stack Exchange. The ng-app directive can be set on any element, and the directives associated with that module will be applied on all elements that are within that element's scope. Without the ng-app directive, Angular does not know which module to run on the page.

//app.js - this defines the module, it uses two parameters to tell the injector what to do.
angular.module('MyApp',[]);

//directive.js stored elsewhere
//this calls back the module that has been created. It uses one parameter because the injector is no longer needed.
angular.module('MyApp').directive('myDirective', function () {
       return {
         restrict: 'AE',
           scope: {
             name: '@'
         },
         template: '<h1>{{name}}</h1>'
       };
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    
<div ng-app="MyApp">
<h1>Successful Load</h1>
<my-directive name="test"></my-directive>
<p>By applying the directive definition to the MyApp module, the MyApp module knows to activate the directive within this scope. In this form, it does not get injected.</p>
</div>

Using Injection

When you have a different module for each and every directive or controller, each one must be injected into the application's module definition. This leaves a lot of room for error. As a best practice, only create a new module when necessary, and make the module a container for a group of related functionality and not a single item.

The code below demonstrates proper injection.

angular.module( "MyApp", ['ReusableDirectives'] );
angular.module( "MyApp" ).directive( "myDirective", function(){
  return {
    restrict: "AE",
    scope: { name: "@" },
    template: "<p>This is the directive I defined in the example above. It uses <u>the same module</u> as my main application, because it is not going to be reused over and over again. In fact, I will need it just for this application, so I don't need to complicate things with a new module.  This directive takes an attribute called 'name' and if it is a string allows me to manipulate the string within my templates scope to do things like this: {{'hello ' + name + '!'}}</p>"
  };
} );
angular.module( "ReusableDirectives", [] );
angular.module( "ReusableDirectives" ).directive("reusableDirective", function(){
  return {
    restrict: "E",
    template: "<p>This is a directive that I intend to use in many, many applications. Because I will reuse it so much, I am putting it in a separate module from my main application, and I will inject this directive. This is the only reason that this directive is not in the same module as the one I defined above.</p>"
  };
} ).directive("reusableDirective2", function(){
  return {
    restrict: "E",
    template: "<p>This is a second directive that I intend to use in multiple applications. I have stored it in a module with the first directive so that I can freely inject it into as many apps as I like.</p>"
  };
} )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="MyApp">
  <h1>Successful Load</h1>
  <my-directive name="Johnny"></my-directive>
  <p>By applying the directive definition to the MyApp module, the MyApp module knows to activate the directive within this scope. In this form, it does not get injected.</p>
  <h3>Injected Directives</h3>
  <reusable-directive></reusable-directive>
  <reusable-directive2></reusable-directive2>
</div>

Keep it simple. Define your directives on a single module for your application. Once you have that done and working, if you need the directives again in another application, refactor and experiment with injections at that time after you have some more Angular practice under your belt.

You have a bright future with Angular, keep up the good work!

Solution 4

Your directive must be camel-cased

    .directive('myDirective', function () {

then in your html, your are free whether to call it my-directive or myDirective

Both are valid

<my-directive name="test"></my-directive>
<myDirective name="test"></myDirective>

Solution 5

Just to follow up on this, I had to use the following way to get my directive to work.

<my-directive name="test"></my-directive>
Share:
18,833
Jochen van Wylick
Author by

Jochen van Wylick

Solution-focused, analytic and communicative are key characteristics that describe me. I love working in a dynamic environment that is constantly challenging me to find solutions. I can work independently without losing touch with the team and I'm not afraid to take on a leading role. My expertise is IT but I have a broad interest and am very eager to learn and expand my knowledge and skill set.

Updated on June 16, 2022

Comments

  • Jochen van Wylick
    Jochen van Wylick almost 2 years

    I've got a tiny problem with an angular directive that's now working and I don't know why. I think it's a fairly simple issue that I'm overlooking, maybe you can help me out.

    Directive is defined like this:

    angular.module('directives', [])
        .directive('my-directive', function () {
            return {
                restrict: 'AE',
                scope: {
                    name: '=name'
                },
                template: '<h1>{{name}}</h1>'
            };
        });
    

    Then index.cshtml:

    <my-directive name="test"></my-directive>
    

    Application.js:

    var app = angular.module('MyApp', [
        ...,
        'directives'
    ]);
    

    And here's controllers.js

    angular.module('controllers', ['apiServices', 'directives'])
        .controller('homecontroller', function($scope, $resource, webApiService, $log, $translate, $localStorage, $sessionStorage) {
    

    Ok confirmed that directives.js is loaded, otherwise application.js nags about 'unknown module'. There are no error messages in the console, the thing just doesn't show. Any ideas?


    EDIT

    So as pointed out, I changed the directive name to camelCase, but still no luck:

    <my-directive name="John Doe"></my-directive>
    

    And

    .directive('myDirective', function () {
    

    But nothing is showing yet.

    EDIT

    Problem is that angular expects an object to be passed into the attribute, not a string literal. If you create an object person = { name: 'John' }, pass the person in, then write {{ person.name }} ( assuming we named the attribute person + scope var person too ).

  • Jochen van Wylick
    Jochen van Wylick over 10 years
    Thanks a lot - so I changed that, but still no luck.
  • Jochen van Wylick
    Jochen van Wylick over 10 years
    Thanks a lot - so I changed that, but still no luck.
  • Jochen van Wylick
    Jochen van Wylick over 10 years
    It's quite large app already, not super easy to create a fiddle. No errors in the console. The fiddle that I created and pasted into the app ( jsfiddle.net/QLBga ) is works great on fiddler.
  • AlwaysALearner
    AlwaysALearner over 10 years
    @spike I have added a fiddle.
  • AlwaysALearner
    AlwaysALearner over 10 years
    @spike To keep things simple you can just have a single module myApp rather than having multiple modules for controllers, directives, etc. Just a thought.
  • Jochen van Wylick
    Jochen van Wylick over 10 years
    That's a great idea, but I am trying to place things in their own files. So are you saying that if I have angular.module('foo', .. ) in multiple files, the injector will merge these into 1 foo module?
  • AlwaysALearner
    AlwaysALearner over 10 years
    @spike Inside directive.js you can do app.directive("bla", funtion
  • Jochen van Wylick
    Jochen van Wylick over 10 years
    It has to do with scope. Because in your fiddle, you see that 'bla bla' is displayed. It's supposed to say 'test', since I wanted to map the scope.name to the 'name' attribute, hence the '=name'
  • AlwaysALearner
    AlwaysALearner over 10 years
    If you want test then you can do $attrs.name inside the directive's link function.