Angular application that gathers data from multiple screens

I am working on a app that switches between different screens. Each screen collect data from the user by means of user selecting a button and clicking on it or selecting a radio button. There will be at least 5-6 screens. i need ideas on how and where to store the data collected and what's the best way to move between different screens: switch what is displayed inside a div on the page or go to an entirely different page. The data on each screen is coming from the different database tables.

So far I have one screen done - it pulls data from the database(via Web API call) and based on the data pulled, displays a different image in the hyperlink.

What I want to do is capture which hyperlink is clicked by the user and store that value somewhere(a location that will persist until all data is collected and stored in the database) and then move on to the next screen. I am sort of stuck here and need some help and ideas.

here is what i got so far:

UI for screen 1:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Types</title>
    <script src="../../Scripts/angular.js"></script>
    <script src="TypesCtrl.js"></script>
</head>
<body ng-app="myApp">
    <div ng-controller="myController">
        <h3>Select a type</h3>
        <br />
        <table>
            <tr>
               
                <td ng-repeat="Type in Types">
                    
                    <a href="#"><img src="Images/type1.png" ng-show="Type.TypeId=='1'" /></a>
                    <a href="#"><img src="Images/type2.png" ng-show="Type.TypeId=='2'" /></a>
                    <a href="#"><img src="Images/type3.png" ng-show="Type.TypeId=='3'" /></a>
                    <a href="#"><img src="Images/type4.png" ng-show="Type.TypeId=='4'" /></a>
		</td>

              
		</tr>
        </table>
    </div>
</body>
</html>

Open in new window


and here is my angular controller:

(function () {
    angular.module("myApp", []).controller("myController", TypeCtrlFunction);
   
    TypeCtrlFunction.$inject = ["$scope", "$http"];
    function TypeCtrlFunction($scope, $http) {
        $http.get('http://localhost:49358/api/myAPI/GetTypes').
        then(function (result) {
            $scope.DeviceTypes = result.data;
        });

    };
})();

Open in new window


I'd appreciate some help to get me moving forward
LVL 35
YZlatAsked:
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.

GreggCommented:
One design consideration that comes to my mind is the need to share your data between controllers. See:

Passing Data between controllers:
https://thinkster.io/a-better-way-to-learn-angularjs/services
https://egghead.io/lessons/angularjs-sharing-data-between-controllers

Making UI aware of changes in data that are shared between controllers:

http://brandonclapp.com/using-angularjs-services-to-broadcast-messages-between-controllers/

These links should help you understand how to address your comment:
i need ideas on how and where to store the data collected and what's the best way to move between different screens

Also, in your code you can eliminate the repeating <img ng-show=""> tags with the following:
<td ng-repeat="Type in Types">
    <a href="#"><img ng-href="Images/type{{Type.TypeId}}.png" /></a>
</td>

Open in new window

YZlatAuthor Commented:
Thanks Gregg, I will take a look at those links.
As far as eliminating repeating image tags, I can't use that because in reality filenames are very different, not just type1.png, type2.png
BigRatCommented:
The keyword here is "data". The data is to be collected and probably sent to the server in one call. Therefore designing the data is priority number one.

I'd put all the data into one big JSON object so that I can call it in one HTTP call and do the store just as easily.

I would not use multiple controllers - that's just too complex. Each screen would be a DIV whose display is dependant on a "pageNo" property in the JSON data :-

$scope.data = {
   pageNo : 1,
   screeen1: (),
   screeen2: (),
   screeen3: (),
   screeen4: (),
   screeen5: (),
}

Open in new window


Each <div> has an ng-show attribute which tests $scope.data.pageNo against its own number :-

   <div ng-show="data.pageNo=2>...........</div>

The fields for the elements in the view and then data.screen2.xyz.

If you want next and previous buttons on the "pages" these should be active according to data.pageNo being in range and should, on click, update pageNo accordingly. Export a couple of functions from the controller to do this.

This is a typical Survey or Quizz application of which there are several ways of doing it :-
http://odhyan.com/blog/2014/12/building-a-simple-quiz-app-using-angularjs/
https://github.com/cdeng/angularFire-survey
http://www.codeproject.com/Articles/860024/Quiz-Application-in-AngularJs
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

YZlatAuthor Commented:
@BigRat, could you please explain? I am new to Angular. I started usinf routing and views to switch between different page - I created partial pages: screen1.html, screen2.html, and screen3.html with just some controls that would go into the main div on the index.html page. I have the routing as follows:

app.config(function ($routeProvider) {
    $routeProvider

        // route for the home page
        .when('/', {
            templateUrl: 'loginScreen.html',
            controller: 'mainController'
        })

         // route for the about page
            .when('/screen1', {
                templateUrl: 'screen1.html',
                controller: 'mainController'
            })

            // route for the contact page
            .when('/screen2', {
                templateUrl: 'screen2.html',
                controller: 'mainController'
            })

            // route for the login page
            .when('/screen3', {
                templateUrl: 'screen3.html',
                controller: 'mainController'
            });

});

Open in new window


and planning, per your advice, to use one controller to handle all the code. Now on my loginScreen.html page all i have is a form with a textbox and a button. I need help handling the onclick event on that page - to grab the value in the text box, store it somewhere and move on to screen1.html. Can you help me with that?
BigRatCommented:
I don't quite see why one needs routing for something so simple.

If you insist on login, then the first page displayed is the login page. This collects username and password and on validation you go to page 1. So the contruction at the beginning of the controller is

$scope.username ='';
$scope.password = '';
$scope.pageNo = 0;

Open in new window


The View for the login is in a <div> which has an ng-show="$scope.pageNo===0"
Just remember to attach the username and password <INPUT>s to the scope with ng-model.
And to have a simple button for submit.

Now while pageNo remains zero all the other divs with the question pages will remain hidden until the login suceeds.

Next we export a function to do the login
$scope.loginUser= function () {
   // set up an http request for a login, passing $scope.username and $scope.password to the server.
}

Open in new window


Importantly if the login suceeds, you set $scope.pageNo to 1. This will hide the login page and display the first question page.

When you come to post the answered questions back to the server you can use $scope.username and $scope.password to perform the request (authenticate and post the data in one call).

You should mock all this upp, before doing HTTPs to the server. Just write a login routine like this -

$scope.loginUser= function () {
   if(($scope.username==='bigrat')&&($scope.password==='cheese')) {
      $scope.pageNo = 1;
   }
}

Open in new window


In fact if do a mockup you can mock up the POST at the end and get all the client side code working before calling the server. This will make testing extremely easy!
YZlatAuthor Commented:
This collects username and password and on validation you go to page 1

Open in new window


How can I accomplish that? Is it done within the controller? Could you please help me with some sample code?
BigRatCommented:
There is no code, AngularJS has two way binding,

<label for="usaename">Password</label>
<input type="text" name="username"ng-model="username" />
<label for="password">Password</label>
<input type="password" name="password" ng-model="password" />
<button ng-click="logInUser()">Login</button>

Open in new window


You can make this very fancy later, by adding in a span element to contain an authentication error message, and such. But for the moment keep it simple.
YZlatAuthor Commented:
Actually no password is neeeded, I just need a username and a button:

<form method="get" id="frmLogin" action="">
    <input ng-model="username" type="text" id="txtUsername" />
    <br />
    <input type="submit" value="Login" id="btnLogin" ng-click="LoginUser()"/>
    <br />
    <span id="lblMsg"></span>
</form>

Open in new window


Now the code for LoginUser() would be in my controller?

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

app.controller('mainController', function ($scope) {
    LoginUser()=function(){
         //code here to go to another page?
           validateUser = function () {
                       
                if (data.isValidUser) {    
                    $window.location.href = '/screen1.html';
                }
                else
                    alert('Login incorrect');
            
        }

    }
});

Open in new window

BigRatCommented:
You must export the function like this :-

app.constant("maxPages",10);

app.controller('mainController', ['$scope','maxPages',function ($scope,maxPages) {

$scope.username ='';
$scope.password = '';
$scope.pageNo = 0;

$scope.LoginUser=function() {
   if(($scope.username==='bigrat')&&($scope.password==='cheese')) {
      $scope.pageNo = 1;
   }
}

// can display a page?
$scope.canDisplayPage = function(page) {
   return page===$scope.pageNo;
}

// next button handler
$scope.nextPage = function() {
   if($scope.pageNo<maxPages) {
      $scope.pageNo = $scope.pageNo+1;
   }
}

}]);

Open in new window


In your view you don't neeed any id numbers, you don't need any forms :-

<!-- Page 0 - the login page -->
<div ng-show="canDisplayPage(0)">
    <input ng-model="username" type="text"/>
    <br />
    <input type="submit" value="Login" ng-click="LoginUser()"/>
    <br />
    <span></span>
</div>

<!-- Page 1 first set of questions -->
<div ng-show="canDisplayPage(1)">
.......................
<button ng-click="nextPage()">next page</button>
</div>

<!-- and so on and so forth -->

Open in new window


You will replace the bigrat login with a proper one once the Angular code is finished. You don't need any routing and under no circumstances do you do things like $window.location.href.

Note how to define a constant, and also note how I injected the dependancies into the controller. If you continue to develop this app - which is a single page app which is ideal for mobile devices as well as browsers, you can minify the resultant code for better performance and easy distribution.
YZlatAuthor Commented:
@BigRat, I tied that but now when I click the button and the second div becomes visible, the login div remains visible too

Also in the second div I will be displaying some data returned from web API:

<div ng-show="canDisplayPage(1)">
        <table>
            <tr>
               
                <td ng-repeat="Type in Types">

                    <a href="#"><img src="Images/abc.gif" ng-show="Type.TypeId=='1'" /></a>
                    <a href="#"><img src="Images/def.png" ng-show="Type.TypeId=='2'" /></a>
                    <a href="#"><img src="Images/ghi.png" ng-show="Type.TypeId=='3'" /></a>
                    <a href="#"><img src="Images/jkl.ico" ng-show="Type.TypeId=='4'" /></a>
                </td>


            </tr>
        </table>
        <button ng-click="nextPage()">next page</button>
    </div>

Open in new window


I also added more code to the controller and now it stopped working:

(function () {
    var app= angular.module("myApp", []);
    
    app.constant("maxPages", 10);


app.controller('mainController', ['$scope', '$http', 'maxPages', function ($scope, $http, maxPages) {

    $scope.username = '';
    $scope.pageNo = 0;

    $scope.LoginUser = function ($scope, $http) {
        alert($scope.username)
        if ($scope.username === '222222') {
            $scope.pageNo = 1;

            //get types
            $http.get('http://localhost:49358/api/myApp/GetTypes').
            then(function (result) {
                $scope.Types = result.data;
            });
        }
    }

    // can display a page?
    $scope.canDisplayPage = function (page) {
        return page === $scope.pageNo;
    }

    // next button handler
    $scope.nextPage = function () {
        if ($scope.pageNo < maxPages) {
            $scope.pageNo = $scope.pageNo + 1;
        }
    }

}]);

})();

Open in new window


P.S. Could you please take a look at my other question?

http://www.experts-exchange.com/questions/28922870/Web-API-stopped-working-ERR-CONNECTION-REFUSED.html
BigRatCommented:
Where's the entire View? The login page must also be enclosed in a div and have an ng-show which queries for page 0. All of the "page" divs should be enclosed in a div which connects to the controller.

A few minor points.

1) When ever you use data it ought to be initialised in the construction. Thus your "Types" ( for Type in Types). Since it is an array you should have $scope.Types =[]; at the beginning of the controller. This stops Angular evaluating "Type in undefined" on start up. Instead it will evaluate "Type in []". The difference is minor, but if you press F12 in Chrome you'll see an error since Types is undefined. By initialising it, you'll get s clean start.

2) Tables render very badly and I'd suggest you use Bootstraps Grid columns, if you want tabular data. http://www.w3schools.com/bootstrap/bootstrap_grid_examples.asp I suppose that this is just a test, since I cannot see why you are producing anchor tags.

3) Once again I'd remove the HTTP call and mock the data by hand. Ir is MUCH easier to test like that. If you run into problems and start posting here again, nobody will know what 'http://localhost:49358/api/myApp/GetTypes is supposed to return. But if in a mockup it is like :-

    $scope.Types = [
         {"one": 1},
         {"two": 2},
         {"three": 3}
    ];

Open in new window


it will be easy to test.

So, replace the HTTP call with mocked data and test the complete application BEFORE going to the server. It is probably the HTTP call which is stopping it working. Don't try to run before you can walk.

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
YZlatAuthor Commented:
Thank you!
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
Angular

From novice to tech pro — start learning today.