Show & Hide some elements based on input - radio buttons

Hi folks,

I got a list of items I display with a helper that uses #each. There’s two categories of the listed items, and; I want to display either or all of them based on the radio buttons relevantly selected.

So what’s the best way to display dynamic data based on user’s selection of inputs (e.g. radio buttons) ?

Note: I’d rather not use jQuery at all, neither to get the selected button; nor to return the necessary action. I’d like to know the most meteoric way to handle this. Thanks.

Edit: Note.

What I unsuccessfully tried is this (didn’t really expect it to work though…)

My Template:

<template name="myTemplate">  
    <div>
      <input type="radio" name="myBut" value="showAll" checked="checked">All
      <input type="radio" name="myBut" value="showToMe">To Me
      <input type="radio" name="myBut" value="showByMe">By Me
    </div>
      {{#each myHelper}}
          {{list-item}}   
      {{/each}}
</template>  

My Helper:

Template.myTemplate.helpers({  
  myHelper: function () {
    if ($("input[name='myBut']:checked").val() === "showToMe") {
      return Requests.find({sent: "toMe"});
    } else if ($("input[name='myBut']:checked").val() === "showByMe") {
      return Requests.find({sent: "byMe"});
    } else {
      return Requests.find({});
    }
  }
});

Thanks!

{{> list-tem}}

This inserts a list-item for each of the documents found in your cursor rather than being a Spacebars reference to either a helper. Hope this helps. =)

Template.template.events({
	'change': function (event) {
		console.log(event.currentTarget.value);
		// use this value to set a reactive var
		// then use that var in the helper
	}
});
2 Likes

Thanks @benjick!

Haven’t used the ReactiveVar package in my project yet. So will definitely give it a try! It seems unavoidable for such cases I guess…

One q please: Is this -the- ideal way? Can Tracker alone (without the ReactiveVar pck) not handle this?

Also, I was expecting to trigger the action connected to a change event. But doesn’t it get a bit unnecessarily too complicated to use at the end: 1.Template, 2. Helper, 3.Event …? Isn’t it just better with jQuery hide() & show() actions etc… maybe? Sorry, I’m only willing to speculate so I find the best practice and therefore also learn well…

Cheers!
Emin

Thanks! Though then I will still need to define the list-item template somewhere which will need to get data, so it becomes the same thing.

My issue is not really about the template, more about to generate data based on user actions…

But perhaps UI.dynamic templates are what I need?? Like this: https://www.discovermeteor.com/blog/blaze-dynamic-template-includes/

For this small application it might seem simpler, but when you use jQuery like that it’s not modular to your template and it’s going to give you headaches when you add another radio button in another template with the same name etc etc. It just doesn’t scale very well imo.

I would do something like this, keep in mind this is just me freestyling here, it’s untested

Template.myTemplate.onCreated = function () {
	this.options = new ReactiveVar({});
}

Template.myTemplate.events({
	'change': function(event, template) {
		switch(event.currentTarget.value) {
			case showToMe:
				template.options.set({sent: "toMe"})
				break;
			case showByMe:
				template.options.set({sent: "byMe"})
				break;
			default:
				template.options.set({})
				break;
		}
	}
})

Template.myTemplate.helpers({
	myHelper: function () {
		return Requests.find(template.options.get());
	}
});

it is better when show/hide is done using templating.
regarding that ReactiveVar, I like to use simple type ones.
This approach from above is object based and if I remember correctly, there is no checking before invalidating.
So simple string could be set 3x to same value and it will not invalidate.
This ‘template’ in events helper seems strange, it is some alias? cause you are not passing it as ‘event’ argument.

BTW regarding jquery, you can match element as template.$(’.something’) and it will be scoped to template, so no problems with more same classes . This is also preffered way to match things, better than whole DOM search $(’.something’)

What do you mean by this?

Missed that, added

An important property of ReactiveVars — which is sometimes a reason for using one — is that setting the value to the same value as before has no effect; it does not trigger any invalidations. So if one autorun sets a ReactiveVar, and another autorun gets the ReactiveVar, a re-run of the first autorun won’t necessarily trigger the second. By default, only primitive values are compared this way, while calling set on an argument that is an object (not a primitive) always counts as a change. You can configure this behavior using the equalsFunc argument.

Is this needed in this case? I haven’t used it like that so I don’t really know

Well, exactly in this case not.
But if you start it as object, ppl tend to add properties there and use on xyz other places :smiley:

Thanks for the reply @shock! Though I wonder what you mean with this, and what is your suggestion exactly? Can you just mention pls? I don’t expect you to right the whole code though…

In any case, I need to set the helper to show data according to the selected button, which, in turn, necessitates that I create an event object to handle constant tracking and pass it to the helper. Right?? In that case, how would your code differ than that of @benjick’s?

Cheers.

Yes, the approach with helper returning some value and using #if in templase is the right approach.
@benjick also used ‘template’ in helper without definition, but principle was right.
I am not sure if there should be ‘this’ or Template.instance() so I use instance :smiley:
There is no need for break in default
OCD rulezz

Code I would use looks like this if ‘this’ could not be used instead of 'Template.instance()'
I never know which way to use it, always just try

Template.myTemplate.onCreated = function () {
	this.options = new ReactiveVar(null);
}

Template.myTemplate.events({
	'change': function(event, template) {
		switch(event.currentTarget.value) {
			case showToMe:
				template.options.set("toMe")
				break;
			case showByMe:
				template.options.set("byMe")
				break;
			default:
				template.options.set(null)
		}
	}
})

Template.myTemplate.helpers({
	myHelper: function () {
		return Requests.find(Template.instance().options.get());
	}
});
1 Like

OK perfect. Thanks. I’ll try when I get home and return with a comment on how it worked :blush:

Hello there again!

Now I got it to work. Thanks a lot! Though I had to change a few things so I’ll mark them here:

Template.myTemplate.onCreated needs to change to: Template.myTemplate.created

event.currentTarget.value doesn’t get the value of the selected radio button. But this does:

template.find('input:radio[name=myBut]:checked').value

There’s one more issue that I couldn’t figure out. So please help if you can:
Since the reactiveVar is only created on “change” in the event, there is nothing displayed on the page render even if the first radio button is selected (on template markup). Can anybody please point me to how I can render the default case in the first render of the page?

Many thanks!

Template.myTemplate.created is deprecated. You should be using Template.myTemplate.onCreated like you tried originally. However, the syntax you used in your example was incorrect. It should be:

Template.myTemplate.onCreated(function () {
	this.options = new ReactiveVar(null);
});

You could set the initial value of your ReactiveVar to the radio button’s initial value, rather than null.

Hi!

That’s correct! You rule.

I’ve done that exactly, but it didn’t work. I did this:

default: template.showWhat.set({})

But the thing is the whole switch case function runs if there’s a change event - because we declare so. Therefore it doesn’t work… :confused: Where can I initialise the ReactiveVar value without having to rely on a change event on the template?

So, there seem to be a few gaps in information, which is making this hard to understand.

Do you have autopublish included or are you using publish/subscribe - and what/where are your publish and subscribe code?

Your case statement seems syntactically incorrect unless you have declared variables showToMe and showByMe somewhere else. I would have expected to see "showToMe" and "showByMe".

Your template’s helper function will run even before a button is pressed, so it will first be invoked as:

Requests.find(Template.instance().options.get());

so, with options set to {} it will run as:

Requests.find({});

which should return everything (unless there is something awry with your subscription - see earlier pub/sub question).

If you have a minimum reproduction in a repo that would help :smile:

Hello there!
I know what you mean. I write you here below the exact code. I’ve altered the code that @shock have sent. Didn’t note here because I thought it was already conceivable detail:

Template.requestsList.events({
	"change": function(event, template) {
	  var currentUserId = Meteor.userId();
	  var element = template.find('input:radio[name=myBut]:checked').value;
	  switch(element) {
            case "showToMe":
              template.options.set({reqFrom: currentUserId});
              break;
            case "showByMe":
              template.options.set({reqBy: currentUserId});
              break;
            default:
              template.options.set({});
          }
	}
});

So where do you think I do wrong? It’s not showing anything on page render…
Thanks.

To reiterate …

Do you have autopublish included or are you using publish/subscribe - and what/where are your publish and subscribe code?