如何将 $state 变量传递到另外两个兄弟 $state 中?

4

Plnkr: https://plnkr.co/edit/Brmcxd2LjGVJ6DXwuJtF?p=preview

架构:

$login -> $container (包含$dashboard$feed), dashboard包含:$tickers$tags$viewHeader$socialMedia组件。

目标:

tags需要与viewHeadersocialMedia状态进行通信并发送一个标签对象。

期望结果:

Login后,从标签列表中选择一个按钮应该会将该tag发送到ViewHeaderSocialMedia的状态/组件中。

结果:

Login后,从标签列表中选择一个按钮并转到$state dashboard时,ViewHeaderSocialMedia的状态会刷新,但视图模型变量{{ term }}在任何组件中都不会更新为相应的标签。

enter image description here

设计说明:

我试图通过状态、组件、同级组件和视图的混合使用来解决一个问题,即应用程序中的每个组件在任何状态更改时都会刷新/重新初始化。 $state.go('dashboard'...

这不是理想的情况,我希望只有需要刷新的组件才会刷新。

例如:当我选择一个TickerTag时,我不希望Feed组件每次都刷新。但是用户应该能够在Feed组件中执行操作并更新所有其他组件。

这就是为什么我创建了一个container状态来容纳dashboardfeed状态。最初,我把所有东西都放在dashboard状态中,每当$state.go('dashboard'发生时,feed组件也会刷新。如果有更好的方法来解决这个问题,请告诉我!:)

附注: 开始学习使用ui-router进行状态管理,以去除对$rootScope的依赖。到目前为止,我的真正应用程序更加稳定,但有一个讨厌的特性,即每个组件都会刷新/重新初始化。

这里输入图片描述


在下面的截图中,我可以清楚地看到正确的tag被传递到了正确的$states中,但是{{ term }}没有更新。

这里输入图片描述

这里输入图片描述

代码片段(完整代码在Plnkr中)

app.container.html(容器状态)

<div>
    <dashboard-module></dashboard-module>
    <feed-module></feed-module>
    <!--<div ui-view="dashboard"></div>-->
    <!--<div ui-view="feed"></div>-->
</div>

仪表板页面 (仪表板状态)

<div class="jumbotron text-center">
    <h1>The Dashboard</h1>
</div>

<div class="row">
  <tickers-module></tickers-module>

  <!-- Tags component goes here -->
  <div ui-view></div>

  <view-module></view-module>

  <social-module></social-module>
</div>

我使用<div ui-view>来加载标签状态的原因是这是目前初始化标签模块并从股票组件显示标签列表的唯一方法。

股票组件控制器:

tickers.component('tickersModule', {
  templateUrl: 'tickers-module-template.html',
  controller: function($scope, $state) {
    console.log('Tickers component', $state.params);
    $scope.tickers = [
      { id: 1, ticker: 'AAPL' },
      { id: 2, ticker: 'GOOG' },
      { id: 3, ticker: 'TWTR' }
    ];

    $state.go('tags', { ticker: $scope.tickers[0] }); // <---

    $scope.clickTicker = function(ticker) {
      $state.go('tags', { ticker: ticker });
    }
 }

^ 上面的代码允许标签状态加载标签组件元素并根据ticker状态连接标签列表。

var tags = angular.module('tags', ['ui.router'])
tags.config(function($stateProvider) {

  const tags = {
    parent: 'container',
    name: 'tags',
    url: '/tags',
    params: {
      ticker: {}
    },
    template: '<tags-module></tags-module>',
    controller: function($scope, $state) {
      console.log('Tags state', $state.params);
    }
  }

  $stateProvider.state(tags);
})
tags.component('tagsModule', {
  templateUrl: 'tags-module-template.html',
  controller: function($scope, $state) {
    console.log('Tags component', $state.params);
    const tags_model = [
      {
        ticker: 'AAPL',
        tags : [{ id: 1, term: 'iPhone 7' }, { id: 2, term: 'iPhone 8' }, { id: 3, term: 'Tim Cook' }]
      },
      {
        ticker: 'GOOG',
        tags : [{ id: 4, term: 'Pixel' }, { id: 5, term: 'Pixel XL' }, { id: 6, term: 'Chrome Book' }]
      },
      {
        ticker: 'TWTR',
        tags : [{ id: 7, term: 'tweet' }, { id: 8, term: 'retweet' }, { id: 9, term: 'moments' }]
      }
    ];

    function matchTags(ticker, model) {
      return model.filter(function(obj){
        if (obj.ticker === ticker) { return obj; }
      });
    }

    $state.params.ticker = $state.params.ticker || {};
    $scope.tags_model = matchTags($state.params.ticker.ticker, tags_model)[0];

    $scope.clickTag = function(tag) {
      console.log(' Tag clicked', $state);
      $state.go('dashboard', { tag: tag });
    }
  }
});

标签列表中的点击功能:

$scope.clickTag = function(tag) {
  console.log(' Tag clicked', $state);
  $state.go('dashboard', { tag: tag });
}

社交媒体控制器:

social.component('socialModule', {
  templateUrl: 'social-module-template.html',
  controller: function($scope, $state) {
    console.log('Social component', $state.params);
    $scope.term = $state.params.tag.term;
  }
});

viewHeader控制器:

view.component('viewModule', {
  templateUrl: 'view-module-template.html',
  controller: function($scope, $state) {
    console.log('View component', $state.params);
    $scope.term = $state.params.tag.term;
  }
});

组件奇怪的重复

我注意到这个问题,当我点击标签并进入social.component后发现。看起来仪表板组件在瞬间被重新渲染,代替了标签组件:

enter image description here


更新内容,通过在tags中添加{ refresh: true }到$state.go解决了此问题。但是这仍然没有解决我的最初任务,即防止feed.component刷新。所以将使用服务。

https://plnkr.co/edit/R0iwJznOgEwOL7AtU5wP?p=preview

enter image description here


请查看此链接,它应该可以解决您的问题。基本上,您需要解析您的参数。 - Pramod_Para
1
我提供一个不同的例子,可以指导您如何将参数从父状态传递到兄弟状态,这样可以吗? - Pramod_Para
请检查这个 Plunker:http://plnkr.co/edit/8M1zXN0W5ybiB8KyxvqW?p=preview。另一个你可以想到的选择是使用 Angular 服务,并在多个状态之间使用单例。 - Pramod_Para
谢谢,但是那个plnkr没有使用任何状态参数或使用这些参数进行导航。我已经fork了它并正在添加这些内容。另外,是的,我的原始应用程序我使用服务/工厂和URL来管理状态,但是一直遇到很多错误。使用ui-router,我知道ticker、tags等状态应该是什么。只是试图防止所有组件刷新... - Leon Gaban
1
抱歉,今天对我来说是漫长的一天,现在已经凌晨3点了。我一定会在今天更新你的 Plunkr。 - Pramod_Para
显示剩余6条评论
3个回答

2
我已经更新了你的 Plunker。

https://plnkr.co/edit/ywyH7wOmR6loNiAkH9Yb?p=preview

解决方案非常简单。不要使用$state,而是在依赖注入中使用$stateParams,它是一个单例,因此如果你像我在更新后的plunker中引用它:
$scope.params = $stateParams

那么显示值
{{params.ticker.ticker}}

将会双向绑定到$stateParams,并在每次更改状态时相应地更新。
---编辑---
我通过服务添加了另一种解决方案 https://plnkr.co/edit/oQNAHv7tAS8LR9njOUAX?p=preview
.service('awesomeTag', function($rootScope){
  var self = this;
  $rootScope.$on('$stateChangeSuccess', function(_event, _toState, toParams){
    self.tag = toParams.ticker.ticker
  })

})

嘿,Maury,我看到你正在传递ticker,但是你如何将tags.component中的标签传递进来?另外一件事,我在你的plnkr中没有看到社交或视图组件的控制器日志。 - Leon Gaban
$state.go('dashboard', { tag: tag }); 这是需要更新 socialview 中的 $stateParams 标签的操作。目前该 tag 对象未能到达那里。有任何想法吗? - Leon Gaban
1
当然,给我几分钟,刚从午餐回来 ;) - maurycy
没问题! :D 我还在自己研究这个。创建了两个新的 plnkrs https://plnkr.co/edit/P0DIEDxLU4Wlm9L0bcTi?p=preview 和一个更干净的版本:https://plnkr.co/edit/2h6fV5yTjeUqLP3SvbvO?p=preview 还可以看看 dashboard.html。我认为问题在于当我进入状态 dashboard 时,它会在 ui-view 中用整个仪表板再次替换标签组件,只需一瞬间。你必须在 social.component 控制器中设置断点才能看到。 - Leon Gaban
是的,路由器的配置使其闪烁,根本问题在于ticker组件,在控制器初始化中有$state.go。 - maurycy
看一下这个关于状态和加载更多状态的新问题,链接为:http://stackoverflow.com/questions/42956873/how-to-load-a-state-view-into-a-nested-view-of-a-top-level-state - Leon Gaban

2
我更新了你的 Plunker,在这里。你代码中的问题在这里:
view.component('viewModule', {
  templateUrl: 'view-module-template.html',
  controller: function($scope, $state) {
    console.log('View component', $state.params);
    $scope.term = $state.params.tag.term; //culprit line
  }
});

你需要做的是使用$stateParams来检索参数。我已经按照以下方式更改了viewModule中的代码:
controller: function($scope, $stateParams) {
    console.log('View component', $stateParams);
    $scope.tickerObj = $stateParams;     
}

然后相应地更新了view-module-template.html,将tickerObj用如下方式使用:
{{ tickerObj.ticker.ticker }}

社交模块也是同样的情况。
一个简单的例子来理解它是这样的:
$stateProvider.state('stateName', {
    url: '/stateName',
    controller: 'myCtrl',
    params: { //this can also be used to give default values for route params
        obj: null
    }
});

function myCtrl($stateParams) {
    console.log($stateParams);   //will display the obj parameter passed while transitioning to this state
}

$state.go('stateName', {obj:yourObj}); //passing obj as param to stateName state, this can be retrieved using $stateParams. 

这类似于您上一个问题中发生的问题。
我注意到您的代码还有另一个问题。点击任何标签(如转推、时刻、推文)后,它会再次将股票代码更改为"AAPL"。这是预期行为吗?(如果不是,这是因为在单击股票代码后转换到仪表板状态时,所有模块都被重新初始化。引起此问题的行是)
$state.go('tags', { ticker: $scope.tickers[0] });

希望这可以帮到您!更新:我在这里更新了 plunker here。我已将此行注释掉:
$state.go('tags', { ticker: $scope.tickers[0] }); 

默认情况下,每次进入仪表板状态时,都会将ticker设置为apple。现在我将ticker和tag对象传递给视图和社交状态。如果您查看控制台,您将能够获取正确的标记和ticker值到这两个状态。如果您有任何问题,请告诉我。
您可以通过查看控制台日志来验证标记和ticker值是否已传递到视图和社交组件。以下是代码片段: enter image description here

好的,我看到tag对象正在传递到socialMedia组件中。$scope.tagObj被设置了。但是当我点击标签按钮时,它又丢失了...并且暂停调试器后我可以看到Dashboard被加载了两次。我认为这是我的问题,有什么解决方法吗? - Leon Gaban
1
我正在看它。 - clever_bassi
好的,是的,点击功能现在可以工作了,但请注意,在开始时缺少tags组件。此外,现在单击标签,我们可以看到dashboard.html在dashboard.html内重新加载。抱歉,我认为这个应用程序现在太复杂了。请查看这个plnkr,它被简化了,我没有使用ui-view来加载标签,也没有不必要的状态配置。如果我们能让这个工作,这也将关闭这个问题:https://plnkr.co/edit/2h6fV5yTjeUqLP3SvbvO?p=preview - Leon Gaban
让我们在聊天中继续这个讨论。 (http://chat.stackoverflow.com/rooms/138657/discussion-between-clever-bassi-and-leon-gaban)。 - clever_bassi
顺便说一下,我想要检查你的答案,但如果只有 Ticker 进入了 View 和 Social,我无法进行检查。标签需要发送到这些组件中。 - Leon Gaban
显示剩余6条评论

0
发布这篇文章是因为这是解决这个问题的正确方法,也是正确架构应用程序的方式:

https://plnkr.co/edit/MztpsHj9qoRCFUDrREH7?p=preview

在这里,我正确地使用了子状态和命名视图。

enter image description here

container-template.html

<div>
  <div class="fl w100">
      <em>Container component module scope</em>  
  </div>

  <div ui-view="dashboard"></div>

  <div ui-view="feed" class="fl"></div>
</div>

标签状态

const tags = {
  name: 'container.dashboard.tickers.tags',
  url: '/tags',
  params: {
    ticker: {},
    tag: {}
  },
  views: {
    'tags' : {
      templateUrl: 'tags-list.html',
      controller: function($scope, $state) {
        console.log('tags-list controller', $state)
        $scope.ticker = $state.params.ticker;

并指定导航到子状态:

$scope.clickTicker = function(ticker) {
    $state.go('container.dashboard.tickers.tags', { ticker: ticker });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接