Viewmodel child not inheriting parent properties and methods?

According to the viewmodel docs, a child template called will inherit its properties and methods but I find myself having to explicitly call child templates as follows (in Jade):

template(name="issue")
  +issueHeaderRow
template(name="issueHeaderRow")
  h1 {{ issue.title }}

If I call explicitly eg: +templateName issue=issue loading=loading etc, it does work

but seeing as this is being called inside my Issue template I’d have assumed that the child template would inherit?

On closer inspection it appears that my methods work with click: parent.myFunction but helpers and properties don’t appear to be able to be accessed either with or without parent.

Would anyone be able to confirm if this is expected behaviour? Thanks!

How are you assigning the issue property to the issue template?

Via a subscription and helper:

onCreated() {
  this.templateInstance.subscribe('issue', FlowRouter.getParam('id'))
},


id() {
    return FlowRouter.getParam('id')
  },

issue() {
  return Issues.findOne(this.id())
},

What does your publication look like?

You’ll have to excuse it being written in Coffeescript, can switch to JS if need be. It seems to be returning the correct issue data to the issue template but not being passed through to the children

Meteor.publishComposite 'issue', (id) ->
  
  userOptions =
    fields: publicUserFields
  
  find: -> return Issues.find(id)
  
  children: [
    { 
      find: (issue) -> return Clients.find issue.clientId 
      children: [
        { find: (client) -> return Meteor.users.find clientId: client._id, userOptions }
      ]
    }
    { find: (issue) -> return Meteor.users.find _id: issue.userId, userOptions }
    { find: (issue) -> return IssueActions.find issueId: issue._id }
  ]

I think the confusion is that issue is a helper and not part of the data context. It’s the “context” that’s inherited, meaning data passed to the parent via props or properites assigned to the viewmodel.

But the docs only says properties:

View models inherit the properties of the context they’re in. Here are the most common scenarios:

I believe the common way to share methods/helpers between two templates/models by using mixins

Ah, I wonder if that might be it, thanks very much I will try and see if it can work like that although up to now I’ve been using mixins for sort of shared functionality like pagination etc, this feels more model-specific?

Yeah I wouldn’t put something that simple in a mixin, I’d just pass the result down directly as a prop

OK thanks, I’m mainly trying to avoid having giant template files. For example the “header” part of my issue template has lots of different buttons/dropdowns which depend on various helpers defined in the issue template. I’m essentially just looking to break up the code into smaller pieces. eg:

+issueHeader

+issueTable

I suppose I could declare some of the helpers directly in the child template perhaps

What I mean is doing this:

template(name="issue")
  +issueHeaderRow(title=issue.title)
template(name="issueHeaderRow")
  h1 {{ title }}

Then you don’t need to change the helpers on issue and you don’t need to add anything extra to issueHeaderRow

Thanks, that’s what I had been doing, I just ended up passing through 6 or 7 props that way which got me wondering about inheritance or whether the values could be passed through in a simpler way but if this is the way to go that’s fine.

Thanks for your help!

The “inheritance” of props happens when you use #with, #each, or pass properties to the child template. Here are some ways to pass the props:

<template name="issue">
  <h1>{{ title }} {{ name }}</h1>

  {{> issueChild title=title name=name}}

  {{#with explicitProps }}
    {{> issueChild }}
  {{/with}}

  {{#with everythingInIssue }}
    {{> issueChild }}
  {{/with}}
</template>

<template name="issueChild">
  <h2>{{ title }} {{ name }}</h2>
</template>
Template.issue.viewmodel({
  title: "Hello",
  name: "World",
  explicitProps() {
    return {
      title: this.title,
      name: this.name
    };
  },
  everythingInIssue() {
    return this;
  }
});

I recommend against passing the whole parent to the child but the option is there if you think you need it.

3 Likes

That’s great Manuel, thanks so much for the clarification and of course for the package! :+1: