简体   繁体   中英

javascript get value of Mongo field already rendered - Meteor

Hey everyone, thank you very much for your help. Question is edited per suggestions in the comments.

I'm new to Mongo and Meteor.

I have a collection "posts" with a field "slug".

The "post" template is populating correctly with each post's values. Slug value is always something like "my-great-post".

I need to get the text value for the _id's slug, which will be different each time the template is accessed, encode it, write a string, and spit the string back out into the template.

Things tried

  • can't return a value for "this.slug" or "this.data.slug" in either template helpers or onRendered, even though collection is defined and correctly populating spacebars values in the template

  • "this" returns "[object Object]" to console.log

  • app crashes when I try to javascript encode and deliver a string from the helper, probably I don't fully understand helper syntax from the documentation

(I followed advice in the comments to avoid trying to create scripts in the template html, so below is more information requested by everyone helping on this thread)

- Template html -

{{#with post}}

<div class="blog-article">
  <div class="blog-header">
    <div class="left">

      <!-- title -->
      <h1 class="post-title">{{title}}</h1>

      <div class="holder">
        <div class="post-tags">
          <!-- tags -->
           {{#each tags}}
              <span>{{this}}</span>
           {{/each}}
        </div>
      </div>

    </div>

  </div> 

  <div class="blog-post">
    <div class="blog-copy">

      <!-- date -->
      <div class="post-date">{{post_date}}</div>

      <!-- social -->  
      <div class="blog-social">
         <!-- 
               <a class="so-facebook" target="_blank" href="need to encode slug here"></a>
         -->       
       </div>

       <!-- ============== post ============== -->

       {{{content}}}

       <!-- ============ end post ============ -->

     </div>
  </div>
</div>

{{/with}}

- Template js -

Template.post.onCreated(function() {
  var self = this;
  self.autorun(function() {
    var postSlug = FlowRouter.getParam('postSlug');
    self.subscribe('singlePost', postSlug);  
  });
});

Template.post.helpers({

  post: function() {
    var postSlug = FlowRouter.getParam('postSlug');
    var post = Posts.findOne({slug: postSlug}) || {};
    return post;
  } 

   // can't get these working in a helper, out of helper they crash the app 
   // console.log(this.slug);   
   // console.log(this.data.slug);

});

Template.post.onRendered( function () {

   // these do not work 
   // console.log(this.slug);   
   // console.log(this.data.slug);

}); 

db.posts.findOne();

{
  "_id" : ObjectId("576c95708056bea3bc25a91f"),
  "title" : "How Meteor Raised the Bar For New Rapid-Development Technologies",
  "post_date" : "May 28, 2016",
  "image" : "meteor-raised-the-bar.png",
  "slug" : "how-meteor-raised-the-bar",
  "bitlink" : "ufw-29Z9h7s",
  "tags" : [
    "Tools",
    "Technologies"
           ],
  "excerpt" : "sizzling excerpt",
  "content" : "bunch of post content html"
}

If some one can solve this using any method, I will accept answer with joy and gratitude most intense.

The problem is probably with the parent template, rather than this one. The way that Meteor works is that the JS files are separated from the HTML, so don't try to include a <script> tag in the HTML.

The first thing is that you have to load all of your documents into the client. (NOTE: once you've got the hang of that, then you can worry about only loading the documents that you need).

To do that, you need a collection and a publication. By default all collections are automatically published completely, so unless you removed the autopublished module, then I'll assume that it is still loaded.

So let's start with the parent template. In this case, I'm going to just loop through all of the posts in the collection and display them using the innerTemplate.

<template name=parent>
  <ul>
  {{#each post}}
    {{> innerTemplate}}
  {{/each}}
  </ul>
</template>

And now our inner template might look like this:

<template name=innerTemplate>
  <li>{{slug}}</li>
</template>

The end result will be a simple list with each slug.

Now, to link everything together, we need to create a JS file, which will: 1. define the collection on both client and server 2. pass the collection to the parent template

This file should be accessible to both the client and the server.

posts = new Mongo.Collection('posts');

if(Meteor.isClient) {
  Template.parent.helpers({
    posts() {
      return Posts.find();
    }
  });
}

Now, if you want to do something with 'slug' in the JS file, you could do something like this:

if(Meteor.isClient) {
  Template.innerTemplate.helpers({
    upperCaseSlug() {
      return this.slug.toUpperCase();
    }
  });
}

Then, you could refer to upperCaseSlug in your template, like thus:

<template name=innerTemplate>
  <li>{{upperCaseSlug}}</li>
</template>

A few things about Meteor:

You should never see a pattern such as:

<script type="text/javascript">
...some code
</script>

Because Meteor combines all your js files into one big file and includes it automatically in your app. You should never have to declare your own script in this way.

Secondly, you should never have to get the value of a data object by reading the DOM. The data context of each template gives you your data in the variable this .

In either a helper or template event you can refer to this and be assured that you're going to get exactly the data being displayed in that instance of the template.

Having now seen your template code it's now apparent that your template has no data context - you set the data context inside your {{#with post}} and its associated helper but that doesn't end up creating the this you need one level below.

So... @Nathan was on the right track except that he assumed you were iterating over a cursor instead of just looking at a single post.

Take all html you have between your {{#with post}} and {{/with}} and put it in a new template, say postDetail then make your outer template:

<template name="post">
{{#with post}}
  {{> postDetail}}
{{/with}}
</template>

Now your postDetail template will get a data context equal to the post object automatically and your helpers can refer to this safely.

Template.postDetail.helper({
  slugURI{
    return "/"+encodeURI(this.slug);
  }
});

Then in your postDetail template you can get the encoded slug with:

<a class="so-facebook" target="_blank" href={{slugURI}}>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM