Как уведомить родительский компонент об изменении и обновлении представления - PullRequest
0 голосов
/ 12 сентября 2018

Я хочу уведомить родительский компонент от дочернего компонента, чтобы обновить представление в родительском компоненте. Я использую аннотацию @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: Как передать событие из дочернего компонента в родительский объект второго уровня

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

Поведение точно такое же. Функция loadPosts выполнена, но представление не загружается.

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

У меня были следующие настройки:

  • Родительский компонент place_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_event.dart (служба шины событий)

Служба Event Bus устанавливается между 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, которая охватывает более сложные темы, такие как общение между дочерними компонентами и от дочернего элемента к родительскому, но не уверен, если это где-то объясняется, и я просто пропустил это.

0 голосов
/ 12 сентября 2018

Иногда 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
}
...