Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't AngularJS view send data to this re-usable service?

An AngularJS app needs to re-use multiple custom objects. I am concerned about creating cluttered, redundant code that is hard to maintain, so I moved re-usable code to a service that can be concisely injected in the controller for each view and then called directly from each html view without cluttering the controllers with redundant code.

The following image illustrates how the service someclass.js will be re-used, and thus why it is important to isolate as much of its code as possible in one place:

I have developed code that partially accomplishes this on my devbox, and I have posted the code to a plnkr. (Thank you to @shakir for getting this plnkr to recreate the problem.) I am also including the code below.

Though clicking a form submit button does cause the form's handler method in the service to be called, the problem is that the object that should contain the form's data is undefined in the someclass.method1() method that should handle the form's data. What specific changes need to be made to the code below so that the value of the user-submitted data from the form can be manipulated inside the service's form handling method?

The complete requirements for communication between the view and the service (through the controller) are:

1.) someclass.prop1 can have its value printed in the view (this works in the code below), and

2.) the ng-submit of confirmForm can directly call someclass.method1 as a handler and thus minimize the code in someroute.js (this happens in the code below, but the object that should contain the form's data is not defined when someclass.somemethod1() is running)


plnkr code:


Here is the plnkr with the call to the service suggested by @JoaozitoPolo. The current version is able to call the someclass.method1() from the view, but the form data is not passed into the function. So we need to figure out how to pass the form data into the function.


CODE SHOWN HERE ALSO:


The code that (on my devbox) connects the properties and methods of someview.html with someclass.js (but does not send the data from the form into someclass.js during the function call) is included as follows below:

Here is the code for someclass.js:

angular
.module('someclass', ['ngCookies'])
.service('someclass', ['$rootScope', '$http', '$cookies', function($rootScope, $http, $cookies){

this.prop1 = $cookies.get('test1');
this.prop2 = 'nothing';
this.resultphone = {};

this.method1 = function(isValid, resultphone) {
    if(isValid){
        var funcJSON = resultphone;
        funcJSON.wleadid = '1';//resultphone is undefined in debugger here
        alert("just a filler to add breakpoint for debugger");
        $http.post('/some-test-service', funcJSON).then(
            function(response) {
                prop2 = response.data.content;
            }
        );
    }
};

}]);

Here is the code for someroute.js:

angular
.module('someroute', ['someclass'])
.controller('someroute', function($scope, someclass) {

    $scope.someclass = someclass;

});

Here is someroute.html:

someclass.prop1 is: {{someclass.prop1}} <br>
someclass.prop2 is: {{someclass.prop2}} <br>
<div ng-show="someclass.prop1!='yes'">
    <h1>someclass prop1!</h1>
    <div ng-include="'someroute_start.html'"></div>
</div>
<div ng-show="someclass.prop1=='yes'">

    <div ng-show="someclass.prop2=='showfirst'">
        <h1>Include start.</h1>
        <div ng-include="'someroute_first.html'"></div>
    </div>

    <div ng-show="someclass.prop2=='showsecond'">
        <h2>Include second.</h2>
        <div ng-include="'someroute_second.html'"></div>
    </div>

</div>

Here is someroute_start.html, which includes the form that needs to be handled by someclass.js:

    <form name="confirmForm" ng-submit="someclass.method1(confirmForm.$valid)" novalidate>
        Enter some value: 
        <input type="text" name="phonenum1" ng-model="resultphone.phonenum1" required />
        <button type="submit" ng-disabled="confirmForm.$invalid" >Submit</button>
    </form>

For completeness, I am including someroute_first.html, which is as simple as:

<h3>Showing first! </h3>

And someroute_second.html, which is as simple as:

<h3>Showing second! </h3>

The main controller for the app is hello.js, which I am including here in case part of the solution includes initializing someclass:

angular
    .module('hello', [ 'ngRoute', 'someclass', 'home', 'someroute', 'navigation' ])
    .config(

            function($routeProvider, $httpProvider, $locationProvider) {

                $locationProvider.html5Mode(true);

                $routeProvider.when('/', {
                    templateUrl : 'home.html',
                    controller : 'home'
                })
                .when('/someroute', {
                    templateUrl : 'someroute.html',
                    controller : 'someroute'
                }).otherwise('/');

                $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

            }
    )

For completelness, index.html loads the modules as follows:

<!DOCTYPE html>
<html>
  <head>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"></script>
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />

    <link href="style.css" rel="stylesheet"/>
    <style type="text/css">
    [ng\:cloak], [ng-cloak], .ng-cloak { display: none !important; }
    </style>
  </head>

  <body class="ng-cloak" ng-cloak="" ng-app="hello">
    <div class="container" ng-controller="navigation">
      <ul role="tablist" class="nav nav-pills">
        <li ng-class="{active:tab('home')}">
          <a href="#/">home</a>
        </li>
        <li ng-class="{active:tab('someroute')}">
          <a href="#/someroute">some route</a>
        </li>
      </ul>
    </div>
    <div class="container" ng-view=""></div>

    <script type="text/javascript" src="someclass.js"></script>
    <script type="text/javascript" src="home.js"></script>
    <script type="text/javascript" src="someroute.js"></script>
    <script type="text/javascript" src="navigation.js"></script>
    <script type="text/javascript" src="hello.js"></script>
  </body>
</html>

Again, all of the above code is included in a bare minimum excerpt of code required to reproduce the problem at this plnkr. So what specific changes need to be made to 1.) get the plnkr to work and 2.) get the form data to be passed into the call to someclass.method1()?

like image 535
CodeMed Avatar asked Nov 09 '22 20:11

CodeMed


1 Answers

I look your plunker updated. Excellent.

Only an issue on someclass.js... you need to use "$this" variable at internal functions to access the correct scope:

$http.post('/some-test-service', funcJSON).then(
    function(response) {
        $this.prop2 = response.data.content;
    }
);
like image 104
Joao Polo Avatar answered Nov 14 '22 23:11

Joao Polo