user4410715
user4410715

Reputation:

Angular JS function not defined error

I am learning AngularJS from AngularJS.org video tutorials but when I run my code it says

Error: [ng:areq] Argument 'ReviewController' is not a function

I got undefined in my console but my code looks the same as in the video here is my html

<!doctype html>
<html ng-app="store">
<title>Testing AngularJS</title>
<head>
    <link rel="stylesheet" type="text/css" href="bootstrap.css"/>
    <script type="text/javascript" src="js/angular.js"></script>
    <script type="text/javascript" src="app.js"></script>
</head>
<body ng-controller="StoreController as store">
<ul class="list-group">
    <li class="list-group-item" ng-repeat="product in store.products">
        <h3>{{product.name}}
            <em class="pull-right">{{product.price | currency}}</em>
            <img class="gallery" ng-src="{{product.images[0].full}}"/>
        </h3>

        <section ng-controller="PanelController as panel">
            <ul class="nav nav-pills">
                <li ng-class="{active: panel.isSelected(1)}"><a href ng-click="panel.selectTab(1)">Description</a></li>
                <li ng-class="{active: panel.isSelected(2)}"><a href ng-click="panel.selectTab(2)">Specifications</a>
                </li>
                <li ng-class="{active: panel.isSelected(3)}"><a href ng-click="panel.selectTab(3)">Reviews</a></li>


            </ul>
            <div class="panel" ng-show="panel.isSelected(1)">
                <h3> Description</h3>

                <h4>{{product.description}}</h4>

            </div>
            <div class="panel" ng-show="panel.isSelected(2)">
                <h3> Specifications</h3>
                <blockquote>None yet</blockquote>

            </div>
            <div class="panel" ng-show="panel.isSelected(3)">

                <h3> Reviews</h3>

                <blockquote ng-repeat="review in product.reviews">
                    <b>Stars: {{review.stars}}</b>
                   <p>{{review.body}}</p>
                    <cite>_{{review.author}}</cite>
                </blockquote>
                <form name="reviewForm" ng-controller="ReviewController as reviewCtrl">
                    <blockquote>
                        <b>Stars: {{reviewCtrl.review.stars}}</b>
                        <p>{{reviewCtrl.review.body}}</p>
                        <cite>_{{reviewCtrl.review.author}}</cite>
                    </blockquote>
                    <label for="select">Select</label>
                    <select id="select" ng-model="reviewCtrl.review.stars">
                        <option  value="1">1 star</option>
                        <option value="2">2 star</option>
                        <option value="3">3 star</option>
                    </select>
                    <label for="comments"></label>
                    <textarea id="comments" ng-model="reviewCtrl.review.body"></textarea>
                    <label for="email">Email</label>

                    <input ng-model="reviewCtrl.review.author" type="email" id="email"/>
                    <input type="submit" value="Submit"/>

                </form>


            </div>
        </section>

    </li>


</ul>


</body>
</html>

Here is my app.js file

(function () {

    var app = angular.module('store', []);
    app.controller('StoreController', function () {
        this.products = [
            {
                name: 'Cup',
                price: 2.95,
                description: 'It is the best Buy it fast',

                images: [
                    {
                        full: 'gem.jpeg'
                    }
                ],
                reviews: [
                    {
                        stars: 5,
                        body: 'This is the best gem',
                        author: '[email protected]'
                    },
                    {
                        stars: 1,
                        body: 'easily broken',
                        author: '[email protected]'
                    }
                ]

            },
            {
                name: 'Bottle',
                price: 1.12,
                description: 'It\'s the best bottle',

                images: [
                    {
                        full: 'gem.jpeg'
                    }
                ],
                reviews: [
                    {
                        stars: 2,
                        body: 'Latest gem',
                        author: '[email protected]'
                    }
                ]

            }
        ]; // product is the property of the controller
    });
    app.controller("ReviewController", function () {
        this.review = {};
    });

    app.controller("PanelController", function () {
        this.tab = 1;
        this.selectTab = function (setTab) {
            this.tab = setTab;

        };
        this.isSelected = function (checkTab) {
            return this.tab === checkTab;
        };
    });


})();

Upvotes: 1

Views: 4676

Answers (1)

Michael Hays
Michael Hays

Reputation: 6908

The problem is here:

<script type="text/javascript" src="js/angular.js"></script>
<script type="text/javascript" src="app.js"></script>

Scripts are executed in the order that they are encountered on the page. So angular.js runs before app.js. It will parse the document and try to resolve ng-app (and thereby ng-controller) before app.js has a chance to execute.

There are two easy ways to solve this:

Put app.js inline at the end of your document

Although I did say that scripts are executed in the order they are encountered, angularjs puts its initialization code behind document.isready. It's loaded, so you will have access to angular.module, but it won't execute its bootstrap logic until after your own code has finished loading. This solution is fine for tiny projects, but isn't great when your code grows large.

Bootstrap your application manually

Remove the ng-app="store" from your html code, and at the end of app.js, you will add the following code

angular.element(document).ready(function() {
  angular.bootstrap(document, ['store']);
});`

If you work with angular long enough, you will get very used to this second solution, as it helps us play nicely with dynamic loaders such as $script, RequireJS, et al -- not to mention the very example you've posted!

Upvotes: 2

Related Questions