Semantic-ui rating

I’am trying to use the rating from semantic-ui with no success.

This is how I try to do it

In the custom.semantic.json -

"rating": true

In the js -

Template.my.onRendered(function () {

this.$('.ui.rating')
    .rating({
        initialRating: 0,
        maxRating: 4
    });
});

And in the template -

<div class="ui rating" data-rating="{{my.rating}}"></div>

But nothing shows up in the template. I have also tried with the star class.

Thanks for any help

Do you see <div class="ui rating" ...> in the DOM tree?
Does $('.ui.rating').rating(...) work from console?

Hi

Yes. I can see the div in the DOM tree and $(’.ui.rating’).rating(…) works from console.

is the rating element in your html in any way enclosed in some kind of conditional statement, that would make it appear after template render? That is a typical problem I’ve had in the past

for example

{{#if anyCookie}}
<div class='my cookie'>
...
    <div class='ui rating'></div>
</div>
{{/if}}

a solution for this would be to wrap your HTML inside the statement in a template, or simply move your conditional statement around a smaller block of the element in question, for example:

<div class='my cookie' style='display:{{#if anyCookie}}block{{else}}none{{/if}};'>
...
    <div class='ui rating'></div>
</div>

this would make the rating element initialize correctly, as the conditional state doesn’t directly manipulate multiple elements, but just a property of a parent element :smile: hope this helps!

Thanks, but the div is not in a if statement. And even if I hardcoded the data-rating and put it by itself in the html - no success.

Are you using the semantic-ui grid system? If so, and it’s in the DOM already as you said, then you might need to put the element in a column for it to appear. Something like:

<div class="sixteen wide column">
  <!-- Your rating element -->
</div>

You could also try putting the rating element into it’s own template. This will ensure the onRendered callback fires at the correct time.

rating.html

<template name="rating">
  <div class="ui rating" data-rating="{{rate}}"></div>
</template>

rating.js

Template.rating.onRendered(function() {
  this.$('.ui.rating')
      .rating({
          initialRating: 0,
          maxRating: 4
      });
  });
});

call it using:

{{> rating rate=2}}
1 Like

Here’s how I did it:

Template.myTemplate.onCreated(function() {
  var instance = this;
  instance.rating = new ReactiveVar(0);
});

Template.myTemplate.onRendered(function() {
  var instance = this;
  Meteor.setTimeout(function() {
    // Initialize rating control
    $('.ui.rating')
      .rating({
        initialRating: 0,
        maxRating: 5,
        clearable: true,
        onRate: function(value) {
          instance.rating.set(value);
        }
      });
  }, 0);
});

But putting it in a separate template as @oliverlloyd suggests is also a good thing.

Possibly overcomplicating things (and completely untested) but this is a nice abstraction away from semantic’s module code:

myTemplate.js

Template.myTemplate.onCreated(function() {
  var instance = this;
  instance.rating = new ReactiveVar(0);
});

Template.myTemplate.helpers({
  ratingArgs() {
    const instance = Template.instance();
    return {
      theRating: instance.rating.get(),
      setRate(rate) {
        instance.rating.set(rate);
      }
    }
  }
});

myTemplate.html

<template name="myTemplate">
  {{> rating (ratingArgs)}}
</template>

rating.html

<template name="rating">
  <div class="ui rating" data-rating="{{theRating}}"></div>
</template>

rating.js

Template.rating.onRendered(function() {
  var self = this;
  // Initialize rating control
  $('.ui.rating')
    .rating({
      initialRating: 0,
      maxRating: 5,
      clearable: true,
      onRate: function(value) {
        self.setRate(value);
      }
    });
});

You could even take it further and pass in the options for the rating initialisation as well, which would give you a nice reusable rating component.

1 Like

I’d never call that overcomplicating, I do that for each Semantic UI component and I found this to be a great help in my development.

Thanks oliverloyd. I tested your template solution and it works fine, I will go with that.

Problems again. It seems that this solution is not reactive.

I forgot to reference data in the onRendered callback, should have been:

    Template.rating.onRendered(function() {
      var self = this;
      // Initialize rating control
      $('.ui.rating')
        .rating({
          initialRating: 0,
          maxRating: 5,
          clearable: true,
          onRate: function(value) {
            self.data.setRate(value);
          },
        });
    });

This will be reactive because the rating template is being passed a ReactiveVar instance.rating (passed in as theRating). This is then used here:

    <template name="rating">
      <div class="ui rating" data-rating="{{theRating}}"></div>
    </template>

So all you need to do is use something like instance.rating.set(3) inside the scope of the parent template (myTemplate) and the component will update reactively. The thing that’s nice about this is you’re controlling things from the parent, the rating template is essentially stupid and just does what it’s told without thinking.

Is you’re still stuck you’re best off pasting some code.

More info on this pattern in the guide here: http://guide.meteor.com/blaze.html#smart-components

Thanks a lot for all help oliverlloyd, I really appreciates it. But I have a hard time to get it to work, especial the reactive part and communication between database. I gave up and tried the “barbatus:stars-rating” package instead and everything works perfect so I will use that instead. It can even handle decimals. Maybe I will give it a try later, it’s hard to give up like that but for now I will use the package. Thanks a lot.