Semantic UI React Dropdown: Header Options? - semantic-ui-react

Is there a way to get a selection dropdown with header items in options?
I know of course there is this tag:
<Dropdown.Header icon='tags' content='Filter by tag' />
But this requires the whole dropdown to have custom Dropdown Items and does not work with Semantic UI's "selection" keyword that does a lot of dropdown management for me and let me give the options over an object array ("options").
When adding/ declaring my options I would like to be able to define it as a header item. Like this:
{
key: id,
value: id,
text: name,
content: (
<span>
{name}
<Label circular>{points}</Label>
</span>
),
header: true
}
Is there something like that? I can't find anything in the documentation.

You can use a Dropdown and manipulate the array like this:
const options = [
{
component:
Dropdown.Menu,
children:
<Dropdown.Menu scrolling>
<Dropdown.Header content='Filter by tag' />
<Dropdown.Divider />
</Dropdown.Menu>,
},
{
key: id,
text: id,
value: name
},
]

If you want the look of a selection dropdown coupled with the ability to use <Dropdown.Header />, try replacing selection with className='selection':
<Dropdown className='selection'>
<Dropdown.Menu>
<Dropdown.Header content='Header' />
<Dropdown.Item>{'Item'}</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>

Related

ember-bootstrap - rendering "radio" inputs

Using the Ember addon ember-bootstrap I can make a set of radio buttons like this :
{{form.element controlType="radio" label="Fruit Type" property="radio" options=radioOptions optionLabelPath="label"}}
with a Controller that looks like this :
export default Controller.extend({
init() {
this._super(...arguments);
this.radioOptions = [
{
label: 'Citrus',
value: 'C',
inline: true
},
{
label: 'Non-Citrus',
value: 'N',
inline: true
}
];
}
});
The relevant doco is here https://www.ember-bootstrap.com/#/components/forms .
However what I can't do is provide a custom value to each radio button so that I end up with rendered HTML like this :
<label>Citrus</label>
<input type="radio" value="C">
<label>Non-Citrus</label>
<input type="radio" value="N">
I have looked at "Custom Controls" on https://www.ember-bootstrap.com/#/components/forms but I can't see how that applies to this case.
EDIT: Just to be clearer about why I want to do this I want to display the readable label (eg "Citrus") but have the non-readable value ("C") available to send back to the server (because the server thinks in terms of "C" or "N".
It's not essential I could send "Citrus" back and map it around on the server but I just thought this would be very straightforward.
Looking at the part of the doco starting with "You can also just customize the existing control component:" on https://www.ember-bootstrap.com/#/components/forms it does seem like you should be able to do the sort of thing I'm after but the example shown doesn't address the use of a value attribute and I can't figure out how to .
You don't need to have the HTML rendered like that. if you want to access the checked radio, simply it is the property name dot value like radio.value.
Here how to get it in the on submit action:
actions: {
onSubmit() {
alert(this.radio.value)
}
}
I had exactly the same issue but I finally solved it. You have to use form.element in block mode. Why is it also necessary to also write an action to update the value? I have no idea!
In my implementation, I'm using Ember changesets and my property is called usageType. I hope it's clear enough to adapt for your needs.
I'm also using Ember Truth Helpers, which is what the {{eq opt.value el.value}} part is. It sets checked to true if the input value is equal to the current-selected value.
# my-component.js
actions: {
selectUsageType(option) {
return this.set('changeset.usageType', option);
}
}
# usage-type-options.js (imported in component JS)
[{
label: 'First label',
value: 'A'
},
{
label: 'Second label',
value: 'B'
}]
# Etc.
# my-component.hbs
# I'm not using angle-bracket invovation here, oops
{{#form.element
property="usageType"
label="My label" as |el|
}}
{{#each usageTypeOptions as |opt|}}
<div class="form-check">
<input
type="radio"
class="form-check-input"
id="{{el.id}}-{{opt.value}}"
checked={{eq opt.value el.value}}
onchange={{action "selectUsageType" opt.value}}
>
<label for="{{el.id}}-{{opt.value}}" class="form-check-label">
{{opt.label}}
</label>
</div>
{{/each}}
{{/form.element}}

How to dynamically apply classes to objects in ember-cli?

I'm creating an app that has a listing of items and a series of filter buttons at the top. As the user applies different filters, I want the buttons to change style using CSS classes to show them as enabled/disabled.
I want to be able to write something like the code below, but it doesn't work.
{{#each category in category_options}}
<button {{action "filterCategory" category}} {{bind-attr class=":btn-small isFiltered(category):btn-active:btn-inactive"}}>{{category}}</button>
{{/each}}
In this example, isFiltered is a computed property on the controller, and it looks at the query parameters to determine whether the specified category has been applied as a filter.
From the reading I've done, it sounds like you can't pass parameters to computed properties. I've come across answers mentioning helpers, bound helpers, and components, but I haven't been able to sort out which one I need, or how I would apply it in this situation.
EDIT:
To clarify the example, imagine I have a series of buttons that filter on various tags:
Filter for: <Cats> <Dogs> <Rabbits> ... # imagine an arbitrary number of these. dozens, maybe
When a user clicks Cats, it triggers filterCategory, which sets the model.category query parameter to ['Cats']. If he then clicks Dogs, model.category becomes ['Cats','Dogs']
Following the latter case, I want the Cats and Dogs buttons to have the class btn-active.
I would like to define isFiltered like so:
isFiltered: function(buttonname) {
if (this.get('model.categories').containsObject(buttonname)) { # pseudocode
return true;
}
else { return false; }
}
Passing buttonname into the function makes it easy to do the comparison for every button and determine if it's in the filter.
If this overall approach is the wrong way to go about things, what's the right way to do it?
1)As component you can do something like below:
in template
{{#each category in category_options}}
{{category-button category=category selectedCategoies=selectedCategories action="filterCategory"}}
{{/each}}
component template
{{category}}
component
export default Ember.Component.extend({
tagName: 'button',
classNames: 'btn-small',
classNameBindings: 'isFiltered:btn-active:btn-inactive',
isFiltered: Ember.computed('category', 'selectedCategories', function(){
return this.get('selectedCategories').contains(this.get('category'));
}),
click: function(){
this.sendAction('action', this.get('category'));
}
})
2)Or you can make your categories as array of objects like so
[
{name: 'category1', isActive: false},
{name: 'category2', isActive: true},
...
]
And then change isActive flag as you need.
In controller:
categoryObjects: Ember.computed('category_options', function(){
return this.get('category_options').map(function(category){
Ember.Object.create({name: category, isActive: false});
})
}),
actions: {
filterCategory: function(category){
category.toggleProperty('isActive');
return
}
}
And in template:
{{#each category in categoryObjects}}
<button {{action "filterCategory" category}} {{bind-attr class=":btn-small category.isActive:btn-active:btn-inactive"}}>{{category.name}}</button>
{{/each}}
I'm not sure how the rest of your code looks like but in general you would use model hook in your route to get query parameter, process it, if needed, and return with your model, let's say you would return model.category, then in your controller you would have something like this:
isFiltered: function() {
var category = this.get('model.category');
// do whatever you want here with category to return true or false
}.property('model.category')
then in .hbs you would be able to write this:
{{#each category in category_options}}
<button {{action "filterCategory" category}} {{bind-attr class=":btn-small isFiltered:btn-active:btn-inactive"}}>{{category}}</button>
{{/each}}
If you were to do this by your approach, you can get it working by making a Computed Property Macro and then looping over the category_options and creating computed properties as isCategory ( isRed, isBlue etc..)
But this won't be the right way to do it, You need to make those button components, which will accept the category_options and model.category and internally decide whether it should be active or not.

What is the best way of creating loop in go html template?

I'm trying to create a html template for displaying posts via html/template Go package.
I also want to make pagination on my page, to display 5 posts per page.
So I take the post count from my post repository, dividing it by posts per page value and rounding it (ceil). That's the total number of pages with posts currently available.
I pass the total number of pages to my html template.
Now, in my html template I need to display page buttons from 1 to the total number.
In the text/html package there is an awesome documentation about how to work with pipelines, but I didn't find any example of creating simple loop.
I got the solution, but I am not sure it is the good one.
I can pass to template not just the total number of pages, but an array of available pages, so in my template I can do something like:
{{range .pages}}
<div class="page">{{.}}</div>
{{end}}
But maybe there is a better way to do this than passing an array of pages?
I also know about possibility of passing custom functions to template. Could it be a solution?
The rule is that the template must contain the minimal logic possible (and that's the reason why the native functions and controls are so limited into the template package).
You should prepare your data into the controller by putting it into a dedicated struct (to be passed to the template). Then you can display this struct (composed of variables and arrays) into the template by using the range function as you intended to do.
try this, i have do my best...
package main
import "html/template"
import "os"
type data struct {
Url string
Title string
}
type show struct {
Pages []data
}
const html = `<html>
{{range .Pages}}
<div class="page">{{.Title}}
</div>
{{end}}
</html>`
func show_template() {
webpage, _ := template.New("template").Parse(html)
mydata := []data{{
Url: "page-1.html",
Title: "go to page 1",
}, {
Url: "page-2.html",
Title: "go to page 2",
}, {
Url: "page-3.html",
Title: "go to page 3",
}, {
Url: "page-3.html",
Title: "go to page 3",
}}
web_data := show{mydata}
webpage.Execute(os.Stdout, web_data)
}
func main() {
show_template()
}
and this is the result..
<html>
<div class="page">go to page 1</div>
<div class="page">go to page 2</div>
<div class="page">go to page 3</div>
<div class="page">go to page 3</div>
</html>

Knockout JS Using IF statement and With Statement listen for click event

The input field should be empty on page load.
When the user clicks the 'Edit Post' then I call KO click and 'select' function (all working) .. when I do this call the row selected is bound correctly.
Current code automatically binds on page load so the first record is in the input field.
<div data-bind="with: Selected">
<input type="text" data-bind="value: Name" />
</div>
<i title="Edit Post" data-bind="click: $parent.select"></i>
Example hack
<div data-bind="if **click: $parent.select then** with: Selected">
<input type="text" data-bind="value: Name" />
</div>
<i title="Edit Post" data-bind="click: $parent.select"></i>
How do I write a data-bind if 'click' then do 'with: Select' ?
Update
Add example code: http://jsfiddle.net/uC8Vt/70/
Generally you would just want this to work off of the Selected observable. If it is not populated, then it won't render the area. If it is pooulated, then whatever object that Selected holds will be used.
So, when you call $parent.select you would want to populate Selected with your object.
In fact, observables are functions, so unless you need to run other logic, you can even take a shortcut and bind your click directly against the Selected observable. The current data is passed as the first argument, which sets the value of the observable.
You would change the Selected property depending on the item clicked...
So an example viewModel might be like...
var items = [{ Name: 'item1' }, { Name: 'item2' }];
var viewModel = {
items: items,
Selected: ko.observable(items[0])
}
viewModel.select = function(selectedItem) {
// The first arg is the context of the item clicked
// Selected in an observable
viewModel.Selected(selectedItem);
};
Then, as Selected changes... your Name binding will automatically update.

Using Ember.js text field ids for a <label> tag

A label tag is of the form:
<label for="id_of_text_field">
<input type="text" name="example" id="id_of_text_field" />
Where the for tag of the label and the id tag of the text field need to match. I had two ideas to make this work in my Ember.js template:
Idea #1: I tried to make a special binding named field_id to use in both the label and the TextField. I executed this as follows:
<label {{bindAttr for="content.field_id"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.data" id="content.field_id"}}
Unfortunately only the label's id renders correctly. The TextField's id does not render correctly and turns out to be "metemorph... something-or-other".
Idea #2: To somehow conjure the TextField's id and use that for the label tag, but I'm afraid at some point the TextField's id will not be ready when the label tag is rendered. Even if this were not an issue, I do not know how to find the TextField's id from JS.
This is in a template so I will have more than one of these label/TextField pairs.
How can I get the label's for tag to match up with the TextField's id tag with Ember.js?
Thank you!
UPDATE
Using Peter Wagenet's advice and slight modification, I did the following:
<label {{bindAttr for="textField.elementId"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.value" viewName="textField"}}
Using this technique users can now click labels to select TextFields, CheckBoxes, and Selects.
First off, the Metamorph tag is for the label's value. It's not related to the field's id. However, this code still won't work because standard properties don't do anything special with property paths. In this case, the value of id, is literally content.field_id. This is certainly not what you wanted. In normal circumstances, you could use elementIdBinding (id is just an alias for elementId), however the element ids for Ember Views cannot be changed after creation so that approach won't work here.
One possible solution makes use of the viewName property. The viewName property provides a named reference to the view on the parentView. You could then, do the following:
<label {{bindAttr for="view.textField.field_id"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.data" viewName="textField"}}
This won't always solve your problem, but I just wanted to add that simply nesting the input inside the label is often a convenient solution, since it allows you to drop the for attribute altogether (reference).
Here was my solution to this problem from a while back:
App.Widget.TextField = Em.ContainerView.extend({
tagName: '',
type: 'text',
label: null,
value: null,
valueSize: '30px',
childViews: ['labelView', 'inputView'],
labelView: Em.View.extend({
tagName: 'label',
attributeBindings: ['for'],
forBinding: 'parentView.inputView.elementId',
init: function () {
this.set('defaultTemplate', Em.Handlebars.compile(this.getPath('parentView.label')));
this._super();
}
}),
inputView: Em.TextField.extend({
typeBinding: 'parentView.type',
sizeBinding: 'parentView.valueSize',
valueBinding: 'parentView.value'
})
});
Then from within a Handlebars template:
{{view App.Widget.TextField label="Some Label"}}