在这里。我有一些全局导航,它使用routeProvider来交换视图内的外部html页面。在视图中,我设置了一个列表类型的子导航(用ng-repeat创建),用于切换外部html文件中的div。如果我在apppctrl:
中手动设置,我可以让它加载页面//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
但是当我点击包含ng-click的span时。我什么也得不到。我开始认为这是一个范围问题,但当我只放一个ng-click='alert()'时,它也不做任何事情。
我读过其他的帖子,但大多数人似乎把一个ng-click放在一个ng-switch里面,而不是相反。在他们的例子中也没有使用路由。仍然是角的新手,所以可能是我还没有遇到过的东西。
HTML应用程序:
<body ng-app="app">
<header ng-include="header.url" ng-controller="nav"></header>
<article ng-view></article>
<footer ng-include="footer.url" ng-controller="nav"></footer>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-route.js"></script>
<script type="text/javascript" src="js/data.js"></script>
<script type="text/javascript" src="js/model.js"></script>
</body>
外部HTML文件:
<div id="web" class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ng-click="page='{{item.name}}'">{{item.name}}</span>
</div>
</div>
</aside><section ng-switch on="page" class="boxModel">
<div ng-switch-when="Comfort Homes of Athens" id="sandbox" class="container round box whitebg">
<h1>Here is link 1</h1>
</div>
<div ng-switch-when="Sealpak Incorporated" id="sandbox" class="container round box whitebg">
<h1>here is Link 2</h1>
</div>
</section>
</div>
JS:
var app = angular.module("app", ["ngRoute"]);
function nav($scope) {
$scope.templates = templates;
$scope.header = $scope.templates[0];
$scope.footer = $scope.templates[1];
$scope.mainNav = mainNav;
$scope.footNav = footNav;
}
app.config(function($routeProvider) {
$routeProvider.when('/',{
templateUrl: "templates/home.html",
controller: "AppCtrl"
}).when('/templates/web.html',{
templateUrl: "templates/web.html",
controller: "AppCtrl"
}).when('/templates/seo.html',{
templateUrl: "templates/seo.html",
controller: "AppCtrl"
}).otherwise({
template: "This doesn't exist!"
});
});
app.controller("AppCtrl", function($scope) {
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
});
不幸的是,ng-repeat
创建的子作用域彼此是兄弟姐妹,是父控制器(ng-controller="nav"
)的子作用域,而ng-switch
在上的<section>
不是您的ng-controller="nav"
的子作用域,而是AppCtrl
。
你可以尝试ng-click="$parent.$parent.page=item.name"
来理解angular的作用域。
<div id="web" class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ng-click="$parent.$parent.page=item.name">{{item.name}}</span>
</div>
</div>
</aside><section ng-switch on="page" class="boxModel">
<div ng-switch-when="Comfort Homes of Athens" id="sandbox" class="container round box whitebg">
<h1>Here is link 1</h1>
</div>
<div ng-switch-when="Sealpak Incorporated" id="sandbox" class="container round box whitebg">
<h1>here is Link 2</h1>
</div>
</section>
我不建议使用这个解决方案,因为它非常丑陋。@link64的解决方案更好,但我认为model
的继承是如此的隐式,并创建一个紧密耦合的代码。在这里,我提出了另一个解决方案,我希望通过发出一个事件来更好地解决这个问题:
<span ng-repeat='item in webProjects' ng-click="$emit('pageChange',item.name)">{{item.name}}</span>
我不确定angular是否能够在模板中解析$emit('pageChange',item.name)
表达式。如果你遇到任何问题,你可以在你的控制器里面写:
<span ng-repeat='item in webProjects' ng-click="setPageChange(item.name)">{{item.name}}</span>
在你的nav
控制器:
$scope.setPageChange = function (pageName) {
$scope.$emit("pageChange",pageName);
}
在您的AppCtrl
中,监听事件并更新页面。
app.controller("AppCtrl", function($scope) {
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
$scope.$on("pageChange", function (event, newPage){
$scope.page = newPage;
}
});
除了@KhanhTo的答案,我想给你指出另一个工具来代替ngRoute;UI-Router。这不是你最初问题的答案,但这是一个更好的解决方案,完全避免了你的问题。
UI-Router增强了ngRoute的页面路由,更加以状态为中心。您将过渡到具有模板和可选控制器的状态。它发出自己的事件,如$stateChangeStart
或$stateChangeSuccess
。你可以通过函数command $state.go(stateName)
或者指令ui-sref="my.state({name: item.name})
来调用这些状态转换
UI-Router是一个非常强大的工具,我不能在这里详细介绍所有的细节,但是它的文档和社区非常棒。
对代码的简单重写可能如下所示。
web.html模板
<div class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ui-sref="app.web.page({name: {{item.name}})">
{{item.name}}
</span>
</div>
</div>
</aside>
<section class="boxModel">
<div ui-view class="container round box whitebg">
<!-- Page content will go here -->
</div>
</section>
</div>
JavaScript app.config(function($stateProvider) {
$stateProvider
.state('app', {
abstract: true,
template: '<div ui-view></div>', //Basic template
controller: "AppCtrl",
}).state('app.home', {
templateUrl: "templates/home.html",
url: '/home'
}).state('app.web',{
templateUrl: "templates/web.html",
url: '/web'
}).state('app.web.page',{
templateUrl: "templates/page.web.html",
url: '/web/page/:name' //Note here the ':' means name will be a parameter in the url
}).state('app.seo',{
templateUrl: "templates/seo.html",
url: '/seo'
});
});
app.controller('AppCtrl', function($scope){
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
$scope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams){
if(newState.name == 'app.web.page'){
var pageName = newStateParams.name; //Variable name matches
$scope.linkText = fetchPageContent(pageName);
}
});
});
page.web.html模板<h1>{{linkText}}</h1>
<h1>{{linkText}}</h1>
通过这些更改,您将能够重用控制器的相同实例。除了允许分页内容更具可伸缩性之外。
$scopes
说明除了$rootScope之外,每个$scope都有一个父级。当您请求视图中的对象时,它将查看其$作用域以查找引用。如果它没有引用,它将遍历到它的父作用域并再次查找。这种情况一直持续到您到达$rootScope。
如果你给视图中的$作用域赋值,它会把它赋值给当前的$作用域,而不是在$作用域链中查找现有的属性。这就是ng-click="model.page = ..."
工作的原因;它查找$作用域的变化模型,然后分配给页面属性,而ng-click="page = ..."
直接分配给当前$作用域。
控制器重用注意事项
据我所知,ngRoute不支持嵌套视图。当你转到一个新路由时,它会销毁$routeProvider中指定的当前视图和控制器,然后为新视图实例化一个新的控制器。UI-Router支持嵌套状态(即带有child $作用域的子状态)。这允许我们创建一个可以在所有子状态中重用的父控制器。
我认为这可能与对作用域如何工作的一些误解有关。
ng-repeat
创建自己的作用域。当尝试设置page
时,angular会在ng-repeat的作用域中创建它。
在AppCtrl
中,按如下方式在作用域上创建对象:
$scope.model = {};
$scope.model.page = 'Comfort Homes of Athens';//Default value
在你的ng-click上,参考model.page
而不仅仅是页面。然后,Angular会遍历作用域来找到model.page
,而不是仅仅在ng-repeat的局部作用域中创建一个属性。
<span ng-repeat='item in webProjects' ng-click="model.page='{{item.name}}'">{{item.name}}</span>
同时,你的AppCtrl将在每次你改变页面时被重新创建。您可能应该使用服务来保存页面更改之间的状态