Angular 1.5 组件通过 ngRoute 更新父控制器

Posted

技术标签:

【中文标题】Angular 1.5 组件通过 ngRoute 更新父控制器【英文标题】:Angular 1.5 component updating parent controller through ngRoute 【发布时间】:2017-04-18 18:37:15 【问题描述】:

我正在使用 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>

angularController

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 的模型。第一个什么都不做,第二个抛出异常。我究竟做错了什么?

【问题讨论】:

【参考方案1】:

首先showSignIn 是一个原语,因此 Angular 会以与showSignIn="7+2" 完全相同的方式处理它。如果您需要在组件内修改该值,则应使用具有showSignIn 属性的对象。

现在menuSelect 有点强硬,可能 Chrome 控制台显示类似

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

这是因为您只是将angCtrl.menuSelect 的引用传递给组件。

为了从 homePageController 内部执行 menuSelect 函数,您必须(在 HTML 中):

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

然后在组件的控制器中这样调用:

this.menuSelect( myMsg:"Devices" )

HTML中的(myMsg)是调用angular来返回这个引用,然后在执行中我们传递参数 myMsg:"Devices" 来匹配我们刚才做的引用中的参数。 您可以查看this answer,它的解释更详细。

【讨论】:

【参考方案2】:

在阅读您的答案的过程中,我突然犯了一个错误:/home 路由的主页组件应该使用 kebab case show-sign-in menu-select 属性而不是最初编码的 lower-camelCase。

您的建议都奏效了。谢谢。在没有组件的情况下,我使用原型继承来访问父范围内的属性和方法。由于 javascript 原型继承的性质,在父作用域中修改标量的唯一方法是在父作用域上调用 setter 方法。显然,类似的东西在这里也适用。更多关于 prototypal inheritance 在 javascript 中的信息。

这是一个概念验证练习。我想测试我从组件更新父范围中的属性和对象以及在父范围中执行方法的能力。此示例以 2 种不同方式更新父作用域中的标量:

    将其粘贴到一个对象中,绑定到该对象并使用组件中的该对象引用它,就像使用 loginData.showSignIn 布尔值一样 在父范围内构建一个 setter 方法并绑定到该方法并从组件调用 setter,就像使用 menuSelect 完成的一样(实际上只不过是 menuSelection 的 setter)

并演示如何在执行此操作时调用函数。

最终的工作代码是:(主页模板不变)

框架

<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>

angularController

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
    
]);

【讨论】:

以上是关于Angular 1.5 组件通过 ngRoute 更新父控制器的主要内容,如果未能解决你的问题,请参考以下文章

通过属性将数组传递到Angular 1.5中的组件

Angular 1.5 通过使用另一个组件在函数中返回 html 结果

Angular 1.5+ 组件可选单向绑定

如何在 Angular 1.5 中为同一组件使用不同的模板 url

如何在 Angular 1.5 组件中等待来自 UI Router Resolve 的承诺

ngRoute (angular-route.js) 和 ui-router (angular-ui-router.js) 模块有什么不同呢?