HTTP Load with Angular.js

I have an HTML page, which includes an accordion with a list of slider questions in each panel. I hope the page could look like this:

Desired look for the panel
It’s content was build dynamically by AngularJS. I use $http.get() to an external API to get an array of data to build the accordion and sliders. (in the form of a JSON request).

I also have two JavaScript plugins in my file to make the accordion and slider look prettier.

After the data was loaded into my page (about 1 to 2 seconds), I got the basic structure of the page with the correct data in it.

However, the sliders and accordion looked the same as those without using the plugins. Those plugins are not working on my dynamically generated elements.

The page looks like this:

Current Panel
The code for the panel building looks like this:

                            <div class="col-md-9">
                                <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                                    <!--groups-->
                                    <div class="panel panel-default form-group" ng-repeat="group in accordion.groupQues">
                                        <div class="panel-heading" role="tab" id="heading{{group.gID}}">
                                            <h4 class="panel-title">
                                    <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{{group.gID}}" aria-expanded="false" aria-controls="collapse{{group.gID}}">
          # {{group.gID}}</a>
      </h4>
                                        </div>
                                        <div id="collapse{{group.gID}}" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="heading{{group.gID}}">
                                            <div class="panel-body">
                                                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illum laboriosam eius optio magni nesciunt nobis.</p>
                                                <!--questions-->
                                                <div class="form-control" ng-repeat="question in group.qs">
                                                    <div class="col-sm-2">
                                                        <strong>{{question.q_code}}: </strong>
                                                    </div>

                                                    <div class="col-sm-10">
                                                        <input class="slider" name="ay_{{question.qID}}" id="ay_{{question.qID}}" data-slider-id='ex{{question.qID}}Slider' type="text" data-slider-min="1" data-slider-max="10" data-slider-step="1" data-slider-value='{{question.q_value}}' />
                                                    </div>
                                                </div>
                                                <!--end of questions-->
                                                <div class="acc-wizard-step">
                                                    <button class="btn btn-pre" type="reset">Go Back</button>
                                                    <button class="btn btn-primary btn-next" type="submit">Next Step</button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <!--end of groups-->
                                    
                                    <div>
                                        test <input class="slider" name="ex1" id="ex1" data-slider-id='ex1Slider' type="text" data-slider-min="1" data-slider-max="10" data-slider-step="1" data-slider-value='3' />
                                    </div>

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

Open in new window


And the code for the called app.js looks like this:

    app.controller('accordionController', ['$http', function ($http) {
        var accordion = this;
        accordion.questions = [];

        $http.get('https://crucore.com/api_assay.php?s=101&a=questions').success(function (data) {
            accordion.questions = data;
            
            accordion.groups = {};
            angular.forEach(accordion.questions, function (question, key) {

                if (!(question.q_group in accordion.groups)) {
                    accordion.groups[question.q_group] = [question];
                } else {
                    accordion.groups[question.q_group].push(question);
                }
            });

            //console.log(accordion.groups);

            accordion.groupQues = [];
            //e.g [{'gID':'1', 'qs': [q1, q2]}, {...}, ...]
            angular.forEach(accordion.groups, function (questionArray, key) {
                var newObj = {
                    'gID': key,
                    'qs': questionArray
                };
                accordion.groupQues.push(newObj);
            });

            console.log(accordion.groupQues);

        });



    }]);

Open in new window


It appears that the problem has to do with the fact that that the accordion and the slider need to have the certain things defined at load but since the actual data loads after the slider loads (because of the HTTP call), the called data then loads without the slider and accordion being applied.

My associate and I are both new to working with angular.js and we're struggling to find out how to do this. Any insight or examples would be appreciated.

Thanks.

If you need more explanation or more code exposed, let me know.
Paul KonstanskiProject SpecialistAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

BigRatCommented:
I don't recognize the accordion (it doesn't look like IU Bootstrap's) and I'm not certain about the slider.

Do you have a mockup or a fixture for the data? I always start with a data mockup so as to avoid the server side call.

I'd probably need to see what else you load. And is this Angular 2 or 1. It looks like 2's style.
Paul KonstanskiProject SpecialistAuthor Commented:
I'll add the three main files in their entirty.

The index.html file is the main file
The app2.js is the file that statically loads the code that then works.
The app.js file is the file that does an API call to dynamically load the elements. When we try this dynamic load, it doesn't work.
myscript.js is what loads the sliders.

So in answer to your question about a mockup, the app2.js file does that. It first does a server call to get the heading information (thus proving that your dyamic code works).  But then in app2 it does a static load of the values. In this case the sliders work.

But once we switch from app2 to app, the sliders don't work.

Again, keep in mind we're new to angularJS and this may not be the best way to do all of this.
app.js
app2.js
myscript.js
index.html
Slick812Commented:
OK, I will give you my version (opinion) of what you might try to do, The reason that your code in the myscript.js  is not putting the + marks (drop down) on the sections, is that the myscript.js is Loaded BEFORE the accordion elements are created in the page load -
    $(document).ready(function () {

you need to NOT use the myscript.js in this, and move the accordion plus and minus symbol code to the success callback function in the app.js  file, after the elements have been created with -
     accordion.questions = data;

I can NOT test this, but you might try this for the   app.js  code -
(function () {
    var app = angular.module('assaymentApp', []);

    app.controller('HeaderController', ['$http', function ($http) {
        var app = this;
        app.message = {};
        $http.get('https://crucore.com/api_assay.php?s=101&a=info').success(function (data) {
            app.message = data;
        });
    }]);

   
    app.controller('accordionController', ['$http', function ($http) {
        var accordion = this;
        accordion.questions = [];

        $http.get('https://crucore.com/api_assay.php?s=101&a=questions').success(function (data) {
            accordion.questions = data;
            
            accordion.groups = {};
            angular.forEach(accordion.questions, function (question, key) {

                if (!(question.q_group in accordion.groups)) {
                    accordion.groups[question.q_group] = [question];
                } else {
                    accordion.groups[question.q_group].push(question);
                }
            });

            //console.log(accordion.groups);

            accordion.groupQues = [];
            //e.g [{'gID':'1', 'qs': [q1, q2]}, {...}, ...]
            angular.forEach(accordion.groups, function (questionArray, key) {
                var newObj = {
                    'gID': key,
                    'qs': questionArray
                };
                accordion.groupQues.push(newObj);
            });

            console.log(accordion.groupQues);


    $(".acc-wizard").accwizard();

    //accordion plus and minus symbol
    var $active = $('#accordion .panel-collapse.in').prev().addClass('active');
    $active.find('a').append('<span class="fa fa-minus-square pull-left"></span>');
    $('#accordion .panel-heading').not($active).find('a').prepend('<span class="fa fa-plus-square pull-left"></span>');
    $('#accordion').on('show.bs.collapse', function (e) {
        $('#accordion .panel-heading.active').removeClass('active').find('.fa').toggleClass('fa-plus-square fa-minus-square');
        $(e.target).prev().addClass('active').find('.fa').toggleClass('fa-plus-square fa-minus-square');
    });
    $('#accordion').on('hide.bs.collapse', function (e) {
        $(e.target).prev().removeClass('active').find('.fa').removeClass('fa-minus-square').addClass('fa-plus-square');
    });

    //accordion button auto focus
    //focus next
    $('.btn-next').click(function () {
        $(this).parents('div.panel').next().find('textarea').focus();
    });

    //focus prev
    $('.btn-pre').click(function () {
        $(this).parents('div.panel').prev().find('textarea').focus();
    });



    //slider
    $('.slider').slider({
        formatter: function (value) {
            return 'Current value: ' + value;
        }
    });

        });

    }]);

})();

Open in new window


I think this may work? ? BUT there may be other factors that are used that I do not know. so it's just my opinion.
BigRatCommented:
The Angular paradigm is Model/View/Controller. The View is the HTML and the Model is the data which you have mocked in app2.js as questionsAPI. Everything which has to do with the control of the view and distribution of the data belongs in the controller. Furthermore the functionality to do with the elements which the controller manages or has created belongs in the controller. One normally implements functions exported into scope which respond to actions like button click by means of angular directives (ng-click) rather than by navigation "under the hood".  

The code in myscript.js is executed once and only once when the document has been loaded. Since it manipulates items which app controller should control, the code OUGHT to be executed when such a controller is constructed. Since the controller construction is dependant on an HTTP request, the code must therefore be allocated to that section. You should avoid using document ready. Moreover the idea in Angular is that the view and the controller may be used for multiple instances in the same user page. Therefore as Slick812 has suggested eliminate myscripts.js and move the code to the controller.

There are a couple of other points I'd like to bring up. I personally don't like the mix of Angular and JQuery style. The idea in Angular is that there are directives which are applied to the HTML elements which are related to properties or functions in the controller. I'd rather use ng-click rather than $(...).click(function()). Secondly the idea behind the view is that on inspection one can find what the view shows. As an example a bit from my search page where facets are hidden in accordion style
<span ng-class="{'glyphicon glyphicon-chevron-down':isFacetVisible(facet),'glyphicon glyphicon-chevron-up':!isFacetVisible(facet)}"></span>

Open in new window

The controller function isFacetVisible() determines whether this facet is to be displayed. We either get an up chevron or a down chevron accordingly. Thus the class (or better said the icon displayed) is not hidden in the Javascript controller code, but in the HTML and indeed handled by an ng-class directive. My controller does not access the DOM. It primarily exports functions to scope (I'm still a fan of $scope and not the ubitiquous "this") eg: canDisplayResults rather than results!==null, which makes testing (also automatically with Jasmine) very easy.

HTH

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Julian HansenCommented:
Are you seeing anything in the console?

Your screen shot seems to indicate that the library is not applying to the target elements. This is either because
a) A library is missing
b) Your reference to the target controls is wrong
c) There is a javascript error halting execution and preventing the code that binds to the controls from running.

The console will list any error output - that is first port of call.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
jQuery

From novice to tech pro — start learning today.