:( Unexpected article='undefined' appears and breaks down my app


#1

I am working on arabic-russian dictionary, editable by users. And I faced an Issue, that I don’t understand.
I’m using blaze. And there is idea of structure:

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

Modal form opens ArticleForm for insert new Article. And then redirects page to new ArticlePage.
ArticlesList wraps couples of . One of them are visible, it depend on editMode parameter. ArticlePage passes array from one article ([articles.findOne]) to ArticlesList.

When I adds first article redirection works fine and page with new Article opens. There is only one ArticlePage template use, with one article instance. Redirection works from after-insert-hook (collections-hooks).

But when I repeat it at second time, there is an error Exception from Tracker recompute function:

And ArticlePage doesn’t render. And in console I see two ArticlePage calls, first with real data, and second with undefined. I can’t understand, from where appears this “undefined” article. If I reload empty page, it will be rendered well with new article.

UPD:
This error appears only when i’m trying to redirect from /articles/xxxx to /articles/yyyy . Redirect from main page / to /articles/xxxx works fine even many times.

Here is how it looks.


#3

You likely have a problem in one of your template helpers. Maybe you assume data is there when it is still being pushed down or data is simply not there.

Our template helpers usually have checks like this

return field && field.sub

Instead of

return field.sub

#4

Thank you for answer. I can’t understand what happens. In ArticlePage, I pass [article], array with one Article to ArticlesList, and when it happens from main page / , there is only one Article and all renders well . But when it is redirection from article/x to article/y , (from one ArticlePage to another) , in console.log i see Article appears 3 times, and third of them are undefined. I can’t figure out, from where it appears, and only when redirects in some route (but different :id) .
I shared the code too on stackoverflow


#5

Did you console log in the helpers to see what is happening live?


#6

yes.
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'])
  }
});

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>

When I create article from main page ‘/’ , app redirect us to new article page with template “ArticlePage”. And there is only 1 console.log("articlePage article", article); , but when I create new article from article page, while redirection from one article to another (newly created) , there is 3 console.log("articlePage article", article); last of them “undefined”


#7

When article is undefined return empty array []


#8

Why to return empty array, when there are real data and real article, which I should render…
For now I fixed this with this condition:

    if (article === undefined) {
      document.location.reload();
    }

It looks not good like smooth change of route. But it works… And I can’t find the better solution for now…


#9

Don’t do that - follow my example. It will refresh automatically, this is how you are supposed to use reactive subscriptions


#10

I tried your example too. It doesn’t work for me. I have just rendered empty template, with no data.
Yes, there is no template exceptions, no errors in console.log, but also there is no rendered data at all. Because template renders empty data context articles = []


#11

I didn’t understand this part. It will return Boolean value enstead of data, isn’t it?


#12

It will only return field.sub if field exists


#13

This is the type of thing that should be in a Meteor sticky/FAQ


#14

I still can’t understand how it relate to my use case. I want to return article, but there is undefined. Problem is not about i use article.field when it doesn’t exist. Problem is , that the article doesn’t exist at all. And I can’t return whole article.


#15

Do you have an address, when this kind of thing are described? I still have not solution. And want to know more about problem.


#16

No, sorry.

Also, it looks like you are using findOne and then passing the result to another template/helper that expects an array. Kind of a weird solution. Why not just do:

Template.ArticlePage.helpers({
  articles() {
    const id = FlowRouter.getParam("id");
    return Articles.find({ _id: id });
  },
});

That way, you’ll either get an empty array or an array with one item, which should keep your template/helpers happy?


#17

Good idea. But still the same result (
in console.log I see cursor to the right article, but it doesn’t rendered on the screen.


#18

What is in you ‘articleSingle’ publish function?

Or maybe rather: After you create the article from the article page, do you redirect it to the new article? Since you use the router params to feed it, the router must know what you want to display.


#19

You also have the subscription in onCreated - does it ever get destroyed after you’ve created the new article? Is onCreated being re-run?


#20

I see the problem now, your onCreated are indeed run before the router param is ready, simply change your autorun to this:

self.autorun(function() {
    if(FlowRouter.getParam("id")){
     self.subscribe("articleSingle", FlowRouter.getParam("id"));
    }
  }

That way it will subscribe when the data is available.

EDIT: Another variant would be if you let everything be as is, but change onCreated to onRendered


#21

Thanks for idea!

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

console.log returns id of previos article, not of newly created.
Template still doesn’t renders , but at least now I know what happens :slight_smile: