Неожиданная статья = 'undefined' появляется и ломает мое приложение (исключение из функции пересчета Tracker:)

Я работаю над арабско-русским словарем, редактируемым пользователями. И я столкнулся с проблемой, которую я не понимаю. Я использую Blaze. И есть идея структуры:

<ModalForm>
    <ArticleForm>
</ModalForm>
<ArticlePage>
    <ArticlesList>
         <ArticleSingle><ArticleForm>
    </ArticlesList>
</ArticlePage>

Модальная форма открывается ArticleForm для вставки нового Article, а затем перенаправляет страницу на новый ArticlePage,

ArticlesList оборачивает пары <ArticleSingle><ArticleForm>, Один из них виден, это зависит от editMode параметр. ArticlePage пропускает массив из одной статьи ([articles.findOne]) чтобы ArticlesList,

Когда я добавляю, перенаправление первой статьи работает нормально и страница с новым Article открывается. Здесь только один ArticlePage шаблон используется с одним экземпляром статьи. Перенаправление работает от after-insert-hook (collection-hooks).

Но когда я повторяю это во второй раз, возникает ошибка, Exception from Tracker recompute function:

Изображение | 690x111

ArticlePage не рендер, и в консоли я вижу два ArticlePage звонки, сначала с реальными данными, а затем с undefined, Я не могу понять, откуда эта "неопределенная" статья.

Если я перезагрузлю эту пустую страницу, она будет хорошо отображаться в новой статье.

Обновить:

Эта ошибка появляется только при попытке перенаправить с /articles/xxxx в /articles/yyyy, Перенаправление с главной страницы / в /articles/xxxx отлично работает даже много раз.

Вот как это выглядит. ошибка% 20of% 20redirect | 690x353

Код

компонент обрабатывает отображение пар ArticleSingle - ArticleEditForm, для списка статей (на странице поиска, на странице ActiveCorrections и т. д.), а также для ArticlePage, когда он отображает только одну статью, ведьма помещается в массив [Article].

ArticlePage.html

<template name="ArticlePage">
    <div class="container article-page">
        <section class="row">
            {{#if Template.subscriptionsReady}}
                {{> ArticlesList articles=articles}}
            {{else}}
                <p>Loading...</p>
            {{/if}}
        </section>
    </div>
</template>

ArticlePage.js

import { Articles } from "/imports/api/articles.js";

Template.ArticlePage.onCreated(function() {
  var self = this;
  var id = FlowRouter.getParam("id");
  self.autorun(function() {
    self.subscribe("articleSingle", id);
  });
});

Template.ArticlePage.helpers({
  articles() {
    const id = FlowRouter.getParam("id");
    const article = Articles.findOne({ _id: id });
    console.log("articlePage article", article);
    return [article];
  },
  isAdmin() {
    return Roles.userIsInRole(loggedInUser, ['admin'])
  }
});

ArticlesList.html

<template name="ArticlesList">
    <div class="articles-list">
        {{#each articles}}
            <section class="row">
                <div class="col-sm-6 -sticky">
                    <div class="main-article">
                        {{> ArticleSingle}}
                    </div>
                </div>
                <div class="col-sm-6">
                    {{#if showEditForm _id}}
                        {{> ArticleForm }}
                    {{else}}
                        {{#each corrections }}
                        <div class="correction">
                            {{#if showEditForm _id}}
                                {{> ArticleForm }}
                            {{else}}
                                {{> ArticleSingle }}
                            {{/if}}
                        </div>
                        {{/each}}
                    {{/if}}
                </div>
            </section>
        {{/each}}
    </div>
</template>

ArticlesList.js

Template.ArticlesList.onCreated(function() {
  Session.set("showEditFormForArticle", "");
});

Template.ArticlesList.helpers({
  showEditForm(articleId) {
    return articleId == Session.get("showEditFormForArticle");
  },
  corrections() {
    if (this.corrections)
      return this.corrections.map(elem => {
        return { ...elem, _id: elem._id + "-by-" + elem.editedByUserName };
      });
    else [];
  }
});

Template.ArticlesList.events({
  "click .correction .btn-success"() {
    let doc_id = this._id.split("-")[0];
    Meteor.call("articles.accept_correction", doc_id, this);
  },
  "click .correction .btn-danger"() {
    let doc_id = this._id.split("-")[0];
    Meteor.call("articles.reject_correction", doc_id, this);
  },
  "click .main-article .btn-success"() {
    let doc_id = this._id.split("-")[0];
    Meteor.call("articles.accept", doc_id, this);
  },
  "click .main-article .btn-danger"() {
    let doc_id = this._id.split("-")[0];
    Meteor.call("articles.reject", doc_id, this);
  }
});

отображает отдельную статью словаря, с ней слова, заметки, переводы и примеры.

ArticleSingle.html

<template name="ArticleSingle">

<div id="article-{{_id}}" class="panel panel-default article {{#if notPublished}}-opacity-06{{/if}}">
  <div class="panel-heading">
        <div class="panel-title words">
            <div class="label label-info speach-part">{{speachPart}}</div>
            <!-- Глагол 1й породы имеет дополнительную информацию для вывода, поэтому 
            особый шаблон его вывода, например, среднекорневую глассную и масдары -->

                {{#each words}}
                    <div class="note">{{note}} </div>
                    <div class="word -arabic-text-big">{{word}}</div>
                    <div class="collapse transcription">{{transcr word}}</div>
                    {{#if isMiddleHarakat ../middleHarakat @index}}
                        <div class="note middleHaracat" title="среднекорневая гласная настоящего времени">
                                {{../middleHarakat}} 
                        </div>
                    {{/if}}
                {{/each}}
            <button type="button" class="btn btn-default btn-xs btn-transcript" 
                data-toggle="collapse" data-target="#article-{{_id}} .transcription">
                Транскрипция
            </button>
        </div>
  </div>
  <div class="panel-body">
        <ol class="translations">
            {{#each translations}}
                <li class="translation">
                    {{translation}}
                    {{#if examplesCount examples}}
                        <a href="" class="showExamplesButton" data-toggle="collapse" title="примеры" data-target=".examples-{{../_id}}-{{@index}}">
                            <small><i class="glyphicon glyphicon-asterisk"></i> 
                            {{examplesCount examples}}</small>
                        </a>
                        <ul class="examples collapse examples-{{../_id}}-{{@index}}">
                            {{#each examples}}
                                <li>
                                    <div class="example -arabic-text-mid">{{example}}</div>
                                    <div class="translation">{{translation}}</div>
                                </li>
                            {{/each}}
                        </ul>
                    {{/if}}
                </li>
            {{/each}}
        </ol>
        {{#if image.link}}
            <div class="image">
                <img class="img-responsive img-rounded" src="{{image.link}}" alt="ArticleImage">
            </div>
        {{/if}}

        <!-- TAGS -->

        {{#if tagsRoots}}
            <div>
                Корень: {{> TagsSelectedSynonyms tags=tagsRoots removeButton=false}}
            </div>
        {{/if}}
        {{#if tagsSubjects}}
            <div>
                Тематики: {{> TagsSelectedSubjects tags=tagsSubjects removeButton=false}}
            </div>
        {{/if}}
        {{#if tagsSynonyms}}
            <div>
                Синонимы: {{> TagsSelectedSynonyms tags=tagsSynonyms removeButton=false}}
            </div>
        {{/if}}                
        
  </div>
  <div class="panel-footer">
        {{> ArticleMenu }}
        {{> ArticleCorrectionMenu}}
        {{#if showApproveButtons}}
            {{> ApproveButtons}}
        {{/if}}
  </div>
</div>
</template>

ArticleSingle.js

import { Articles } from "/imports/api/articles.js";
import { Subjects } from "/imports/api/subjects.js";
import { transcription, isNotDiacritic } from "/imports/transcription.js";

Template.ArticleSingle.onCreated(function() {
  Meteor.subscribe("subjects");
  Meteor.subscribe("articlesByIds", this.data.synonyms);
  Meteor.subscribe("articlesByIds", this.data.roots);
});

Template.ArticleSingle.helpers({
  showApproveButtons() {
    return (
      Roles.userIsInRole(loggedInUser, ['admin']) &&
      this.published === false &&
      this.deleted !== true
    );
  },
  notPublished() {
    return this.published === false;
  },
  isMiddleHarakat(middleHarakat, index) {
    return middleHarakat && index == 0;
  },
  rootArticle() {
    Meteor.subscribe("articleSingle", this.rootId);
    return Articles.findOne({ _id: this.rootId });
  },
  image() {
    const image = Images.findOne({ _id: this.picture });
    return image;
  },
  imageJSON() {
    const image = Images.findOne({ _id: this.picture });
    image0 = JSON.stringify(image.fetch());
    return image0;
  },
  examplesCount: function(examples) {
    return examples.length || 0;
  },
  transcr: function(text) {
    if (text.trim()) return "[ " + transcription(text) + " ]";
  },
  tagsSubjects() {
    const ids = this.subjects;
    const tags = [];
    let tagsUnordered = Subjects.find({ _id: { $in: ids } }).fetch(); // эта шляпа возвращает массив в смешанном порядке, поэтому их надо заново упорядочить
    ids.forEach(tagId => {
      tags.push(
        tagsUnordered.filter(elem => {
          return elem._id == tagId;
        })[0]
      );
    });
    return tags;
  },
  tagsSynonyms() {
    const ids = this.synonyms;
    const tags = [];
    let tagsUnordered = Articles.find({ _id: { $in: ids } }).fetch();
    // эта шляпа { $in: ids } возвращает массив в смешанном порядке, поэтому их надо заново упорядочить
    ids.forEach(tagId => {
      tags.push(
        tagsUnordered.filter(elem => {
          return elem._id == tagId;
        })[0]
      );
    });
    return tags;
  },
  tagsRoots() {
    const ids = this.roots;
    const tags = [];
    let tagsUnordered = Articles.find({ _id: { $in: ids } }).fetch();
    // эта шляпа { $in: ids } возвращает массив в смешанном порядке, поэтому их надо заново упорядочить
    ids.forEach(tagId => {
      tags.push(
        tagsUnordered.filter(elem => {
          return elem._id == tagId;
        })[0]
      );
    });
    return tags;
  }
});

И что странно, когда я зарабатываю в Template.ArticleSingle.onCreated console.log(это), когда я добавляю статью с главной страницы /, после перенаправления есть только один TemplateInstance, и он хорошо рендерится. Но когда я создаю статью y от article/x страница, после перенаправления с существующей страницы article/x к новому созданному article/y страница, в консоли есть 3 TemplateInstances, треть из них undefined, Я до сих пор не понимаю, откуда это появляется:(

1 ответ

Наконец я нашел ответ (с помощью добрых людей):

ArticlePage.js:

Template.ArticlePage.onCreated(function() {
  var self = this;
  self.autorun(function() {
    const id = FlowRouter.getParam("id");
    if (id) {
      self.subscribe("articleSingle", id);
    }
  });
});

При повторном рендеринге после перенаправления FlowRouter.getParam("id") было undefined и у нас нет подписки. Теперь все работает хорошо.

Другие вопросы по тегам