0

I'm fairly new to AngularJS and am trying to put together a small demo application. The part I'm trying to get working is as follows:

  • User enters stock market code into text field that is two-way bound with ng-model.
  • Directive has a bind-to-click function that retrieves some data from an API.
  • Once data is returned, the directive is compiled and appended to a div.
  • The directive is supposed to accept a text variable through an isolated scope and display it. This is the part that is not working properly.

Code

Directives

financeApp.directive('watchlistItem', function () {
    return {
        restrict: 'E',
        templateUrl: 'app/directives/watchlistItem.html',
        replace: true,
        scope: {
            asxCode: "@"
        }
    }
});

financeApp.directive('myAddCodeButton', ['$compile', '$resource', function ($compile, $resource) {
    return function(scope, element, attrs){
        element.bind("click", function () {
            scope.financeAPI = $resource('https://query.yahooapis.com/v1/public/yql', {callback: "JSON_CALLBACK" }, {get: {method: "JSONP"}});
            //scope.financeResult = 
            scope.financeAPI.get({q: decodeURIComponent('select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22' + scope.asxcodeinput + '.AX%22)'),
                format: 'json', env: decodeURIComponent('store%3A%2F%2Fdatatables.org%2Falltableswithkeys')})
            .$promise.then(function (response) {
                scope.quote = response.query.results.quote;
                console.log(scope.quote);
                angular.element(document.getElementById('watchlistItemList')).append($compile("<watchlist-item asx-code=" + scope.quote.Symbol + "></watchlist-item")(scope));
            }, function (error) {
                console.log(error);
            });
        });
    };
}]);

Directive Template

<div class="watchItem">
    <a href="#/{{asxCode}}">
        <div class="watchItemText">
            <p class="asxCodeText"><strong>"{{asxCode}}"</strong></p>
            <p class="asxCodeDesc"><small></small></p>
        </div>
        <div class="watchItemQuote">
            <p class="asxPrice lead"></p>
            <p class="asxChangeUp text-success"></p>
        </div>
    </a>
</div>

HTML

<html lang="en-us" ng-app="financeApp">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta charset="UTF-8">

    <title>ASX Watchlist and Charts</title>

    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="css/styles.css">

    <script src="https://code.angularjs.org/1.4.5/angular.min.js"></script>
    <script src="https://code.angularjs.org/1.4.5/angular-route.min.js"></script>
    <script src="https://code.angularjs.org/1.4.5/angular-resource.min.js"></script>
    <script src="app/app.js"></script>
</head>
<body>

    <div class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="#/">ASX Watchlist and Charts</a>
            </div>
        </div>
    </div>

    <div class="container-fluid" id="mainContainer">
        <div class="row">
            <div class="floatLeft" id="leftDiv" ng-controller="navController">
                <form class="inline-left">
                    <div class="form-group floatLeft">
                        <label class="sr-only" for="asxinput">ASX Code</label>
                        <input type="text" class="form-control" id="asxinput" placeholder="ASX Code" ng-model="asxcodeinput" />
                    </div>
                    <button class="btn btn-default floatRight" my-add-code-button>Add</button>
                </form>
                <div id="watchlistItemList">
                    <!-- Test item -->
                    <div class="watchItem">
                        <a href="#/AFI">
                            <div class="watchItemText">
                                <p class="asxCodeText"><strong>AFI</strong></p>
                                <p class="asxCodeDesc"><small>Australian Foundation Investments Co</small></p>
                            </div>
                            <div class="watchItemQuote">
                                <p class="asxPrice lead">$6.50</p>
                                <p class="asxChangeUp text-success">+ 5%</p>
                            </div>
                        </a>
                    </div>
                </div>
            </div>
            <div class="floatLeft" id="rightDiv" ng-view>

            </div>
        </div>
    </div>

</body>
</html>

The directive "compiles" and is appended to the DOM element as expected, but the asxCode variable is not interpolating within the directive template.

Any suggestions greatly appreciated.

Seonixx
  • 468
  • 3
  • 16
  • 1
    Could you throw this in a fiddle or something? – ajmajmajma Sep 11 '15 at 13:34
  • it's not really clear what it is you are trying to accomplish here; You have a directive which is trying to use `angular.element` to insert and then compile another directive, but the scope that you are compiling into is the isolate scope of the button directive? Usually, the use of `angular.element` is a code smell; You are trying to manipulate the DOM rather than trying to manipulate the DATA. – Claies Sep 11 '15 at 13:54
  • Like I said, very new to AngularJS. What I'm trying to do at a high level is enter a code into the input field, make an API call based on that input and then append another instance of the directive template with data retrieved from the API call. – Seonixx Sep 11 '15 at 14:00
  • append it *where*? you haven't shown that portion of your code. The *frequently used* method to do this would be to have an array to store your `codes`, and use an `ng-repeat` against the array to output your directive. When a new `code` is added to the array, it will automatically be added to the DOM through the `ng-repeat`. – Claies Sep 11 '15 at 14:03
  • In general, you should always be thinking about your data, and almost never be concerned with manual manipulation of the DOM. Let the angular directives handle the DOM changes for you whenever you possibly can, you'll have clearer, easier to maintain code. – Claies Sep 11 '15 at 14:06
  • Thanks for the advice. Obviously I'm struggling to adjust my thinking for AngularJS over jQuery for example. So is ng-repeat a "binding" so to speak? So the controller can add further data to the array and it will update the DOM? – Seonixx Sep 11 '15 at 14:12
  • 1
    yes, that's exactly right. It is two way bound, and can be filtered, sorted, etc.. I'll try to write out an example in the meantime, the question http://stackoverflow.com/questions/14994391/thinking-in-angularjs-if-i-have-a-jquery-background?rq=1 is a good read. – Claies Sep 11 '15 at 14:49
  • Thanks a lot Claies. Was looking at this the complete wrong way. Got it working in about 20 minutes after a full day of working out my previous issues :( Used ng-repeat and ng-click to dynamically add to the array. – Seonixx Sep 11 '15 at 15:24

3 Answers3

0

You are using scope.asxcodeinput instead of scope.asxCode.

Darjan Bogdan
  • 3,780
  • 1
  • 22
  • 31
  • The scope.asxcodeinput is a value from the API call, which is appended to the directive as an attribute. The attribute text isn't being interpolated within the directive template. – Seonixx Sep 11 '15 at 13:51
0

Try changing this line:

angular.element(document.getElementById('watchlistItemList')).append($compile("<watchlist-item asx-code=" + scope.quote.Symbol + "></watchlist-item")(scope));

to:

angular.element(document.getElementById('watchlistItemList')).append($compile('<watchlist-item asx-code="{{' + scope.quote.Symbol + '}}"></watchlist-item>')(scope));

There are three changes, you were missing the closing > for </watchlist-item"), and isolated scope @ will do interpolation so you need to pass in expression {{}}, and I changed double quotes to single quotes so you can properly set the attribute inside double quotes.

Beyers
  • 8,968
  • 43
  • 56
0

Thanks all,

I was looking at this the complete wrong way. Thanks Claies for recommending ng-repeat. Didn't realise this was part of the digest loop.

Ended up implementing an ng-repeat with an API call that is triggered on ng-click and appends data to the ng-repeat array.

Seonixx
  • 468
  • 3
  • 16