如何通知父组件更改和刷新视图

时间:2018-09-12 12:48:41

标签: dart angular-dart

我想从子组件中通知父组件以更新父组件中的视图。我正在使用@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函数,但视图未加载。

1 个答案:

答案 0 :(得分:0)

我进行了以下设置:

  • 父组件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需要通知子组件列表-post,已经添加了新帖子,并且列表-post应该重新获取与该地点相关的所有帖子。

post_event.dart(事件总线服务)

事件总线服务是在post_new_component和post_list_component之间设置的。

我正在将一个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

我在组件的构造函数中设置了事件侦听器,该侦听器侦听来自新组件的事件更改。每次收到事件时,我都会通过_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);
  }
}

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