Компонент Angular 1.5 обновляет родительский контроллер через ngRoute

Я использую ngRoute для создания одностраничного приложения Angular. Хотите перейти на компонентную версию.

Проблема в изолированных областях. Мне нужен доступ к реквизитам и методам основного контроллера. Пытаюсь использовать привязки, но не работает. Я не могу найти проблему с этим. Это приложение отлично работает без использования компонентов. Когда я пытаюсь изменить представление домашней страницы на компонент, происходит сбой. Вот основные части кода:

фреймворк

<html ng-app="angularModule" >
<body ng-controller="angularController as angCtrl" >
    <div ng-show="angCtrl.user.isLoggedIn" >Sign Out</div>
    <div ng-hide="angCtrl.user.isLoggedIn" cd-visible="angCtrl.showSignIn">Sign In</div>
    <div id="contentLayer" class="contentLayer" ng-view ></div>

шаблон домашней страницы

<h1 class="pageLabel" >HomePage</h1>
<blockquote>This can be anything. No bindings.</blockquote>

угловой контроллер

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

app.directive ('cdVisible', 
    function () {
        return  function (scope, element, attr) {
                    scope.$watch (attr.cdVisible, 
                        function (visible) {
                            element.css ('visibility', visible ? 'visible' : 'hidden');
                        }
                    );
                };
    }
);

app.config ( [ '$locationProvider', '$routeProvider',
    function config ($locationProvider, $routeProvider) {
        $locationProvider.hashPrefix ('!');
        $routeProvider
        .when ('/sign-in', {
            templateUrl:    '/ng-sign-in',
            controller:     signInController
        })
        ... more routes
        .when ('/home', {
            template:   '<home-page showSignIn="angCtrl.showSignIn" menuSelect="angCtrl.menuSelect" ></home-page>'
        })
        .otherwise ('/home');
    }
]);

function homePageController () {
    this.menuSelect ('Devices');  // this statement has no effect on angularController.menuSelection chrome shows it as an anonymous function
    this.showSignIn = false;  // this bombs: Expression 'undefined' in attribute 'showSignIn' used with directive 'homepage' is non-assignable!
}

app.component ('homePage', {
    templateUrl:    '/ng-homepage',
    controller:     homePageController,
    bindings: {
        menuSelect: '&',
        showSignIn: '='
    }
});

app.controller ('angularController', [ '$http', '$window', '$location',
    function ($http, $window, $location) {
        var self = this; 
        this.user = {
            "isLoggedIn":       false
        };
        this.showSignIn = true;
        this.menuSelection = "";
        this.errorMessage = "";
        this.menuSelect = 
            function (selection) {
                self.menuSelection = selection;
            };
        this.setUserData =
            function (userData) {
                self.user = userData;
            };
        this.setShowSignIn =
            function (show) {
                self.showSignIn = show;
            };
        this.menuSelect ('');
        this.getUserData();     // I removed this for this post
    }
]);

Я добавил комментарий к тому месту, где возникает исключение. Контроллер домашней страницы пытается обновить модель angularController. Первый ничего не делает, второй выдает исключение. Что я делаю не так?


person webDeveloperDave    schedule 04.12.2016    source источник


Ответы (2)


Прежде всего, showSignIn является примитивом, поэтому angular будет обрабатывать его точно так же, как и showSignIn="7+2". Если вам нужно изменить это значение внутри компонента, вам следует использовать объект со свойством showSignIn.

Теперь menuSelect немного сложнее, возможно, консоль Chrome показывает что-то вроде

function (locals) {
    return parentGet(scope, locals);
} 

Это потому, что вы просто передаете ссылку на angCtrl.menuSelect компоненту.

Чтобы выполнить функцию menuSelect изнутри homePageController, вам нужно будет сделать (в HTML):

<home-page menu-select="angCtrl.menuSelect(myMsg)"></home-page>

А затем вызовите его так в контроллере компонента:

this.menuSelect({ myMsg:"Devices" })

(myMsg) в HTML — это вызов angular для возврата этой ссылки, затем при выполнении мы передаем параметр { myMsg:"Devices" }, чтобы он соответствовал параметру в ссылке, которую мы только что сделали. Вы можете проверить этот ответ, который объясняет это более подробно.

person GMaiolo    schedule 04.12.2016

В процессе чтения вашего ответа у меня возникла одна ошибка: компонент домашней страницы маршрута /home должен использовать кебаб-кейс show-sign-in menu-select для атрибутов, а не lower-camelCase, как было изначально закодировано.

Ваши предложения оба работали. Спасибо. Без компонентов я использовал прототипное наследование для доступа к свойствам и методам в родительской области. Из-за природы прототипного наследования javascript единственный способ изменить скаляры в родительской области — это вызвать методы установки в родительской области. По-видимому, что-то подобное применимо и здесь. Подробнее о прототипном наследовании в JavaScript.

Это была проверка концепции. Я хотел проверить свою способность обновлять свойства и объекты в родительской области из компонента, а также выполнять метод в родительской области. В этом примере скаляр обновляется в родительской области двумя разными способами:

  1. вставьте его в объект, привяжите к объекту и ссылайтесь на него, используя этот объект из компонента, как это делается с логическим значением loginData.showSignIn
  2. создайте метод установки в родительской области, привяжите к нему и вызовите установку из компонента, как это делается с menuSelect (который на самом деле является не чем иным, как установкой для menuSelection)

и демонстрирует вызов функции при этом.

Окончательный рабочий код: (шаблон домашней страницы не меняется)

фреймворк

<html ng-app="angularModule" >
<body ng-controller="angularController as angCtrl" >
    <div ng-show="angCtrl.user.isLoggedIn" >Sign Out</div>
    <div ng-hide="angCtrl.user.isLoggedIn" cd-visible="angCtrl.loginData.showSignIn">Sign In</div>
    <div id="contentLayer" class="contentLayer" ng-view ></div>

угловой контроллер

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

app.directive ('cdVisible', 
    function () {
        return  function (scope, element, attr) {
                    scope.$watch (attr.cdVisible, 
                        function (visible) {
                            element.css ('visibility', visible ? 'visible' : 'hidden');
                        }
                    );
                };
    }
);

app.config ( [ '$locationProvider', '$routeProvider',
    function config ($locationProvider, $routeProvider) {
        $locationProvider.hashPrefix ('!');
        $routeProvider
            .when ('/sign-in', {
                templateUrl:    '/ng-sign-in',
                controller:     signInController
            })
                ... more routes
            .when ('/home', {
                template:   '<home-page login-data="angCtrl.loginData" menu-select="angCtrl.menuSelect(mySelection)" ></home-page>'
            })
            .otherwise ('/home');
        }
]);

function homePageController () {
    this.menuSelect (   {   mySelection:    'Overview' });
    this.loginData.showSignIn = true;
}

app.component ('homePage', {
    templateUrl:    '/ng-homepage',
    controller:     homePageController,
    restrict:       'E',
    bindings: {
        menuSelect: '&',
        loginData:  '='
    }
});

app.controller ('angularController', [ '$http', '$window', '$location',
    function ($http, $window, $location) {
        var self = this; 
        this.user = {
            "isLoggedIn":       false
        };
        this.loginData = {
            "showSignIn":       false
        };
        this.menuSelection = "";
        this.errorMessage = "";
        this.menuSelect = 
            function (selection) {
                self.menuSelection = selection;
            };
        this.setUserData =
            function (userData) {
                self.user = userData;
            };
        this.setShowSignIn =
            function (show) {
                self.showSignIn = show;
            };
        this.menuSelect ('');
        this.getUserData();     // I removed this for this post
    }
]);
person webDeveloperDave    schedule 04.12.2016