【问题标题】:How to notify parent component of change and refresh view如何通知父组件更改和刷新视图
【发布时间】:2018-09-12 12:48:41
【问题描述】:

我想从子组件通知父组件更新父组件中的视图。我正在使用@Output 注释来做到这一点。

在父组件中,实际上调用了函数“loadPosts()”,但视图没有更新。有人知道为什么吗?

会发生什么:

  • place_component 包含一个“list-post”指令,用于显示所有帖子。
  • place_component 包含一个模式,用于使用指令“new-post”添加新帖子
  • 保存新帖子时,会通过@output 将消息解析回模式中的“new-post”指令:(doneIt)="loadPosts()"
  • loadPosts() 函数已执行,但“list-post”指令未重新加载。

父组件:

place_component.dart:

@Component(
  selector: 'my-place',
  directives: [coreDirectives, 
                formDirectives, 
                PostNewComponent, 
                PostListComponent,
                MaterialButtonComponent,
                MaterialDialogComponent,
                ModalComponent,
                MaterialTabPanelComponent,
                MaterialTabComponent],
  templateUrl: 'place_component.html',
  styleUrls: ['place_component.css'],
  providers: [materialProviders]
)
class PlaceComponent implements OnActivate, OnInit {
  Place place;

  final PlaceService _placeService;
  final Location _location;
  final ChangeDetectorRef cdRef;

  int _id;

  bool showBasicDialog = false;

  final tabLabels = const <String>[
    'Posts',
    'Pictures',
    'Pending Invitations'
  ];

  PlaceComponent(this._placeService, this._location, this.cdRef);

  @override
  Future<void> onActivate(_, RouterState current) async {
    _id = paths.getId(current.parameters);
    loadPosts();
  }

  @override
  Future<void> ngOnInit() async {
    print("init executed");
  }

  Future<void> loadPosts() async {
    if (_id != null) place = await (_placeService.get(_id));
    cdRef.detectChanges();
    print("loaded posts $_id");
  }

  void goBack() => _location.back(); 

  Future<void> save() async {
    await _placeService.update(place);
    goBack();
  }
}

place_component.html:

<div *ngIf="place != null">
    <h2>{{place.name}}</h2>
    <div class="grid">
      <div class="col-1-3">
        <div class="module">
          <material-button class="open-post-button" (trigger)="showBasicDialog = true" [disabled]="showBasicDialog" raised>
            New Post
          </material-button>
        </div>
      </div>
      <div class="col-2-3">
        <div class="module">
          <material-tab-panel class="tab-panel" [activeTabIndex]="0">
            <material-tab label="Posts">
              <div class="posts">
                <div class="post">
                  <list-posts [place]="place"></list-posts>
                </div>
              </div>
            </material-tab>
            <material-tab label="Pictures">
              Pictures
            </material-tab>
            <material-tab label="Videos">
              Videos
            </material-tab>            
          </material-tab-panel>
          <div class="divider10"></div>
        </div>
      </div>
    </div>  
</div>
<modal [visible]="showBasicDialog">
    <material-dialog class="basic-dialog">

      <h1 header>New Post</h1>

      <div class="new-post">
          <new-post (doneIt)="loadPosts()" [place]="place"></new-post>
      </div>

      <div footer>
        <material-button autoFocus clear-size (trigger)="showBasicDialog = false" class="close-button">
          Close
        </material-button>
      </div>

    </material-dialog>
  </modal>

子组件

post_new_component.dart:

@Component(
  selector: 'new-post',
  directives: [coreDirectives, 
                formDirectives,
                FileUploader,
                materialInputDirectives,
                MaterialButtonComponent],
  templateUrl: 'post_new_component.html',
  styleUrls: ['post_new_component.css'],
  providers: [ClassProvider(PostService)]
)
class PostNewComponent {
  final PostService _postService;
  final _onDone = new StreamController.broadcast();

  String postText;
  Post post;

  @Input()
  Place place;

  @Output()
  Stream get doneIt => _onDone.stream;

  PostNewComponent(this._postService);

  Future<void> save() async {
    await _postService.create(postText,place.id).then(((_) => _onDone.add(1)));
  }
}

post_new_component.html:

<div class="post-new-component">
    <div>
        <material-input floatingLabel
            multiline
            rows="2"
            maxRows="4"
            label="Add a new post here...." 
            [(ngModel)]="postText"
            class="post-text">
        </material-input>
    </div>
    <div class="post-buttons">
        <file-uploader class="file-uploader"></file-uploader>
        <div><material-button (trigger)="save()" raised class="send-button">Post</material-button></div>
    </div>
    <div class="clear-float"></div>
</div>

我现在还根据这个例子尝试了一个 EventBus:AngularDart: How do you pass an event from a Child component to a second level parent

  PlaceComponent(this._placeService, this._location, this._postEvent, this.cdRef) {
    _postEvent.onEventStream.listen((int id) => loadPosts().then((_){cdRef.markForCheck();}));
  }

行为完全相同。 loadPosts 函数已执行,但视图未加载。

【问题讨论】:

  • Angular CD 在身份上工作,因此它应该检查 Place 的确切实例是否已更改。您是否碰巧在服务中返回相同的实例?另一种可能性是抛出了一个运行时异常,它破坏了 CD。浏览器控制台有什么东西吗?
  • 谢谢特德,我解决了。我不认为该列表实际上也是它自己的组件。所以通信是从子组件到子组件。一旦我将 EventBus 连接到它工作的另一个子组件。

标签: dart angular-dart


【解决方案1】:

有时Angular在异步调用后不会触发变更检测,你需要使用ChangeDetectorRef强制它

final ChangeDetectorRef cdRef;

PlaceComponent(this.cdRef);

Future<void> loadPosts() async {
 if (_id != null) place = await (_placeService.get(_id));
 ////

 cdRef.markForCheck();

 // or 

 cdRef.detectChanges();

 /// actually I don't know what is the best here
}

【讨论】:

  • 非常感谢您的反馈,两种方法都试过了,但都没有成功。它仍然没有重新加载。
  • detectChanges() 已弃用,请改用 markForCheck()
【解决方案2】:

我有以下设置:

  • 父组件places_component
  • 子组件 post_new_component
  • 子组件 post_list_component

为了解决我的问题,我必须不将事件发送到父组件,而是发送到另一个子组件。所以@Output 是行不通的。我只是将 EventBus 连接到另一个子组件。

所以父组件的html简而言之是这样的:

...
<div><new-post [place]="place"></new-post></div>
<div><list-posts [place]="place"></list-posts></div>
...

所以子组件 new-post 需要通知子组件 list-posts,一个新的帖子已经添加,list-posts 应该重新获取与某个地点相关的所有帖子。

post_event.dart(事件总线服务)

在 post_new_component 和 post_list_component 之间设置了 Event Bus 服务。

我正在将一个 int 传递给 Stream (int id),这现在并不重要,因为我只需要检查是否触发了事件,您还可以解析字符串、对象或其他任何内容,如果您需要随事件发送数据。

@Injectable()
class PostEvent {
  final StreamController<int> _onEventStream = new StreamController.broadcast();
    Stream<int> onEventStream = null;

    static final PostEvent _singleton = new PostEvent._internal(); 

    factory PostEvent() {
         return _singleton;
    }

    PostEvent._internal() {
         onEventStream = _onEventStream.stream;
    }

    onEvent(int id) {
         _onEventStream.add(id);
    }
}

post_new_component.dart

添加帖子后,执行_postEvent.onEvent(1)。如上所述,“1”并不重要,因为我只想知道是否触发了事件。

@Component(
  selector: 'new-post',
  directives: [coreDirectives, 
                formDirectives,
                FileUploader,
                materialInputDirectives,
                MaterialButtonComponent],
  templateUrl: 'post_new_component.html',
  styleUrls: ['post_new_component.css'],
  providers: [ClassProvider(PostService), ClassProvider(PostEvent)]
)
class PostNewComponent {
  final PostService _postService;
  final PostEvent _postEvent;

  String postText;
  Post post;

  @Input()
  Place place;

  PostNewComponent(this._postService, this._postEvent);

  // Save a new post
  Future<void> save() async {
    // Create a new post and then fire a post event to notify the post list component to update itself.
    await _postService.create(postText,place.id).then(((_) => _postEvent.onEvent(1)));
  }
}

post_list_component.dart

我在组件的构造函数中设置了事件侦听器,它侦听来自 post-new 组件的事件更改。每次收到事件时,我都会通过 _getPosts() 函数获取所有帖子。

@Component(
  selector: 'list-posts',
  directives: [coreDirectives, formDirectives],
  templateUrl: 'post_list_component.html',
  styleUrls: ['post_list_component.css'],
  providers: [ClassProvider(PostService), ClassProvider(PostEvent)]
)
class PostListComponent implements OnInit {
  final PostService _postService;
  final PostEvent _postEvent;
  List<Post> posts;

  @Input()
  Place place;

  PostListComponent(this._postService, this._postEvent) {
    // listen for postEvents, if received re-fetch posts
    _postEvent.onEventStream.listen((int id) => _getPosts());
  }

  // Get all posts when page loads the first time
  void ngOnInit() => _getPosts();

  // Function to get all the posts related to a place
  Future<void> _getPosts() async {
    posts = await _postService.getPostsByPlace(place.id);
  }
}

如果有人知道更好的方法,请以任何方式纠正我,因为我对框架还不太熟悉,并且很难理解这些概念。该文档涵盖了很多内容,但是如果有人像我一样对框架完全陌生,那么我会丢失一些信息。对英雄文档的扩展将不胜感激,它涵盖了更复杂的主题,例如子组件之间的通信以及子组件与父组件之间的通信,但不确定是否在某处进行了解释,我只是错过了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-22
    • 2014-01-12
    • 1970-01-01
    • 2016-05-07
    • 2020-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多