1

I'm making an app in AngularJs and I'm facing a situation where I need to call the same function in multiple views.

In this case, it's a store system where the same store can be visible in multiple views. In the home, if it's near the user, in a paid ad, in the search result, etc..

I know this function should be in a service, which I already did. But the question is: How should I call this function from the view/html?

I'd like to avoid re-define the same calling over and over in multiple controllers, but until now, didn't found anything related to this specific problem.

This is the code I have:

function mainFactory($http) {
    var service = {
        addFav: _addFav
    };
    return service;

    function _addFav(data, func) {
        return $http.post(baseUrl+func,data).then( 
            function(res) {return res.data;},
            function(err) {alert(_errorMsg);}
        );
    };
}

function HomeController() {
    vm.addFavorites = addFavorites;
    function addFavorites(data) {
        var func = 'function_name';
        mainFactory.addFav(data,func);
    }
}

function AdvController() {
    vm.addFavorites = addFavorites;
    function addFavorites(data) {
        var func = 'function_name';
        mainFactory.addFav(data,func);
    }
}

function SearchController() {
    vm.addFavorites = addFavorites;
    function addFavorites(data) {
        var func = 'function_name';
        mainFactory.addFav(data,func);
    }
}

As you can see, I need to call the same function in multiple controllers.

I already saw some other examples saying to use this function as a $rootScope function from the .run, but also way more content warning about not being a good practice to define functions as root functions.

Any suggestion on how to solve this?

Edit:

Other solution I'm using right now, is to have a 'MainController' defined in the body tag via directive, so i can use this function in the whole app. But this controller is getting way to complex to maintain, with a lot of functions/process.

celsomtrindade
  • 4,501
  • 18
  • 61
  • 116
  • 1
    http://stackoverflow.com/questions/15025979/can-i-make-a-function-available-in-every-controller-in-angular – mrvol Dec 21 '15 at 15:33
  • I don't think service is the answer, define the function in the top of $scope would be ok, $scope.foo = function () {}, then use it in your view like – Tom Dec 21 '15 at 15:37
  • My question is different, I already saw that topic. But what I want to avoid is to re-call the function in every controller. I'd like to define it in a single controller and be able to call in multiple views. – celsomtrindade Dec 21 '15 at 15:37
  • you have to declare it somewhere and to inject either the rootScope or your Service dependency. The other solution is a non-angular one, to create a basic function on a JS file, and to add this file in your pages, but i'm not even sure you can call it from the HTML view in this case. Unfortunatly I don't see any others ways. The problem here if you do a 'parent' controller will be that you'll need to manage multiple controllers per views, and it's not recommended – Alex Dec 21 '15 at 15:40

2 Answers2

4

How should I call this function from the view/html?

You shouldn't do it directly from the view.

But, you can nest controllers into other ones. So, if you think that your function deserved to be a "special" one, you can have the following "scenario".

html :

<div ng-controller="appController as app">
    <div ng-controller="HomeController as home"> 
         <button ng-click="app.addFavorites();"></button>
    </div>

    <!-- ... -->
</div>

This can also work with SPA if you put your appController away from the "inner view". Feel free to give this "appController" another name (mine sucks a bit).

Deblaton Jean-Philippe
  • 11,188
  • 3
  • 49
  • 66
0

Suppose you have an app like that:

<html ng-app="app">

<body ng-controller="MainController">

<div ui-view></div>

</body>
</html>

And then you have app like:

angular.module('app', ['app.models']).controller('MainController', MainController);

function MainController($scope, MyService) {

    $scope.addToFav = function (data) {

        // suppose data is object
        // then 

        MyService.add({
           url: "...",
           data: data
        }).then(function(result) {

             // success

        }).catch(function(error) {

            // wrong

        });
    }
}

// this is a service, shared by controller
(function (angular) {

    'use strict';

    angular.module('app.models')
        .factory("MyService", _model);

    _model.$injector = ['$http'];

    function _model($http) {

        var service = {
            all: all,
            one: one,
            update: update,
            add: add,
            remove: remove
        };

        return service;

       function add(param) {

            var url = param.url;

            if (url === null) {
                return null;
            }

    $http({
        url: url,
        method: 'POST',
        data: $httpParamSerializerJQLike(param.data),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
       }).then(function (result) {
                return result;
       });
    }
})(angular);

Then you can use MyService in any of your controller, and you can use addToFav to any of your view in same app...

<a ng-click="addToFav(...)"></a>

If you concern about the bloat issue of MainController, the better way is use local controller as the vm:

function ExampleController($scope, Product) {

  var vm = this;

  vm.product = ProductComponent.singleton($scope, Product)

}

var ProductComponet = (function () {

    var instance;
    var scope;

    return {
        singleton: function (_scope, Model) {

            scope = _scope;

            if (!instance) {
                instance = createInstance(Model);
                console.log(instance);
            }

            console.log(instance);

            return instance;
        }
    };

    function createInstance( Model) {

       var vm = {
         item: {},
         list: [],
         addToFav: addToFav
       };

       return vm;

       function addToFav() {

       }
    }
})();

Then in your view, you can use:

vm.product.item to access current Item

vm.product.list to access all items

Keep all related data and method in one component, and re-use it in different controller.

Then why not use angular service or factory, coz they are all singleton, yes, we can use both, the idea is to build component, and re-use it rather than just use controller

Tom
  • 4,612
  • 4
  • 32
  • 43
  • This is what I have at the momment. But in my case, I have multiple segments I need to do the same thing. So my MainController is getting bigger, with a lot of functions. So I'm looking forward to optimize this. But this is a great way to do, if I don't find other solution – celsomtrindade Dec 21 '15 at 15:59