简体   繁体   中英

AngularJS - Run function of child controller from parent controller

I have read many questions on this but still can't find solution that will be clean and reusable enough.

What I tried and don't want to use:

  • Events - $emit and $broadcast,
  • DOM manipulation - for example angular.element('nameOfElement').scope() to access scope of child,
  • $onChange in child component

Simple example

I've build simplified scenario of to present the problem.

On page there is TimerComponent that shows minutes and seconds. It have controller TimerController to change its state: start() and stop() . In example there are two of them, both presenting different values.

Timer template does not contain buttons to manipulate it - that is intentional . I want to start and stop the timer from other controller.

Child-To-Parent communication . I can pass value and function callback onTick() that will be run every second.

Parent-To-Child communication : How could I run start() or stop() functions from parent component?

timer.component.js

(function() {
    'use strict';

    TimerController.$inject = ['$interval']
    function TimerController($interval) {
        var ctrl = this;

        var interval = undefined;

        ctrl.start = start;
        ctrl.stop = stop;
        ctrl.minutes = minutes;
        ctrl.seconds = seconds;           

        function minutes() {
            return Math.floor(ctrl.value / 60);
        }

        function seconds() {
            return (ctrl.value % 60);
        }

        function start() {
            interval = $interval(function() {
                ctrl.value--;
                ctrl.onTick({ 'value': ctrl.value });
            }, 1000);
        }

        function stop() {
            $interval.cancel(interval);
        }
    }

    angular.module('app').component('timer', {
        bindings: {
            value: '<',
            onTick: '&'
        },

        templateUrl: 'timer.html',
        controller: TimerController
    });
})();

timer.html

<span ng-bind="$ctrl.minutes() + ':' + $ctrl.seconds()"></span>

app.html

<body ng-app="app" ng-controller="MainController as vm">
    <p>First timer: </p>
    <timer value="360" on-tick="vm.firstTimerTick(value)"></timer>

    <p>Second timer: </p>
    <timer value="120" on-tick="vm.secondTimerTick(value)"></timer>

    <p>
        <span ng-click="vm.startFirstTimer()">Start first timer</span>
    </p>
</body>

main.controller.js

(function() {
    'use strict';

    angular.module('app').controller('MainController', MainController);

    function MainController() {
        var ctrl = this;
        ctrl.firstTimerTick = firstTimerTick;
        ctrl.secondTimerTick = secondTimerTick;
        ctrl.startFirstTimer = startFirstTimer;

        function firstTimerTick(value) {
            console.log('FirstTimer: ' + value);
        }

        function secondTimerTick(value) {
            console.log('SecondTimer: ' + value);
        }

        function startFirstTimer() {
            /* How to run start() function of TimerController here? */
        }

        function stopFirstTimer() {
            /* How to run start() function of TimerController here? */
        }
    }
})();

One approach is to use the new ngRef directive :

<timer ng-ref="vm.timerOne" value="360" on-tick="vm.firstTimerTick(value)">
</timer>

Then use it in the controller:

function startFirstTimer() {
    /* How to run start() function of TimerController here? */
    ctrl.timerOne.start();
}

The ngRef directive was introduced as a new feature with AngularJS V1.7.1 .

For more information, see AngularJS ng-ref Directive API Reference .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM