I'm creating a tag suggestions function, depending of a category. So, I have a select box with a bunch of categories, when I select a category, I want to display the sub-categories (using an array obviously) in a list. Here's what I have now:
<select id="categorySelect">
<option value="6">Animal</option> //the value here is the category id
<option value="12">Music</option>
</select>
<ul id="suggestedTags">
</ul>
my JSON array:
var tagsMakers= [
{ category: 'Animal', suggestedTags: [
{ name: 'cat'},
{ name: 'dog' },
{ name: 'rabbit'}
]},
{ category: 'Music', suggestedTags: [
{ name: 'rock' },
{ name: 'rap' }
]}
];
$("#categorySelect").change(function(){
});
I'm still learning array manipulations, and I don't know where to start!
In words the logic is:
When I select a category, I want to display every suggested tags for that category in the li below. I also want to be able to chose multiple categories, so if I select both categories, I want the suggested tags for both to show.
Anyone have a little time to help?
Yeah, first bind an event to the select tag, and based on the value, display the list. Also remove the JSON structure, and build a hash instead:
tagMakers = {'Animal': ['Cat','Dog','Rabbit'], 'Music': ['rock','rap']}
$("#categorySelect").change(function() {
$("#suggestedTags").empty();
$(this).find(":selected").each(function() {
var selected = $(this).text();
$.each(tagMakers[selected].function(i,n) {
$("#suggestedTags").append("<li>"+n+"</li>");
});
});
});
Your array of tags doesn't have the category id, so I'll use the category name here. But, as a good practice, better if you put the category id in the categories array. Better yet if you use a object hash instead of an array.
function getCategoryByName(name){
//search in array.
for(var i = 0, len = tagsMakers.length; i < len; i++)
{
if (tagsMakers[i].category === name)
{
// found.
return tagsMakers[i];
}
}
// do not exists
return;
}
$("#categorySelect").change(function(){
// get the selected value
var current = $(this).val(),
suggestedTags = $("#suggestedTags"),
category;
// we do not have the category id on 'tagsMakers', so
// we need the category name.
current = $("option[value="+current+"]", this).text();
//search in array.
category = getCategoryByName(current);
//populate the suggested tags
suggestedTags.empty()
$.each(category.suggestedTags, function(i, tag) {
$("<li>" + tag.name + "</li>").appendTo(suggestedTags);
});
});
jsFiddle: http://jsfiddle.net/vcZnu/
EDIT: If you can change your categories array by an object hash (better), so you can use the solution provided by #CupidVogel, otherwise use this, which resolve your problem as asked in your question.
Related
I've implemented a suggestion items for an input field inside a table and it's working fine. Also, i've implemented the event "SuggestionItemSelected" to get the additional text of the suggested item to display the description of the code.
SuggestItemSelected event will trigger only if we select the suggested item from the list and then we could get the text of the code selected.
So how to get the text of the entered code if we enter the data directly in the input field without using suggestion items? I know we could get the value of the input field by reading "byId()" but not getting how to get the associated text.
I've tried to filter the suggestion items with the selected key value but it wouldn't work. Kindly help to filter the suggestion items on key value to get the associated text of it.
Suggestion Items Array
I played a bit around with the suggestions and it looks like if:
you type very fast and press enter before suggestion opens, onSuggestionItemSelected is not called.
leave (mouse click somewhere else) onSuggestionItemSelected is not called.
You could simply check getSelectedKey()and refuse this user input with a message like "use a suggestion".
Anyhow, there is a way to filter afterwards based on the input for a suggestion item key; see my example.
But keep in mind that getSuggestionItems() returns 0 items if the suggestions http-request is still in progress. Sadly ui5 has no promise api in place.
sap.ui.controller("view1.initial", {
onInit : function(oEvent) {
const oModel = new sap.ui.model.json.JSONModel();
oModel.setData({
rows : [
{ key: "1", col4 : "Value4" },
{ key: "2", col4 : "Value2" },
{ key: "3", col4 : "Value16" },
{ key: "4", col4 : "Value20" }
]
});
this.getView().setModel(oModel);
},
onChange: function(oEvent) {
const input = oEvent.getSource()
let selectedKey = input.getSelectedKey()
if(!selectedKey){
var dataObject =
selectedKey = input.getSuggestionItems()
.map( (suggestionItem)=> suggestionItem.getBindingContext().getObject() )
.find( (a)=> a.col4 === oEvent.getParameter("newValue") );
if(dataObject){
selectedKey = dataObject.key
console.log("found key:" + selectedKey)
}else{
console.warn("no matching key")
}
}else{
console.log("provided key:" + selectedKey)
}
},
onSuggestionItemSelected: function(oEvent) {
}
});
sap.ui.xmlview("main", {
viewContent: jQuery("#view1").html()
})
.placeAt("uiArea");
/* extra CSS classes here */
<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-libs="sap.m"></script>
<div id="uiArea"></div>
<script id="view1" type="ui5/xmlview">
<mvc:View controllerName="view1.initial" xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc">
<Input
showSuggestion="true"
change="onChange"
suggestionItemSelected="onSuggestionItemSelected"
suggestionItems="{/rows}">
<suggestionItems>
<core:Item text="{col4}" key="{key}" />
</suggestionItems>
</Input>
</mvc:View>
</script>
I have selected tag input and I can choose multiple items using MagicSuggest plugin.
The problem is that the value that I get after the choice is a string separated without a comma.
Like:
MVCPHPASP.Net
I want to change it to:
MVC,PHP,ASP.Net
The result that I get when I choose multiple items :
The string that I get when I alert :
So how can I do that?
The right way is to get the data using the plugin's API, not weird parsing.
According the docs you can use the function .getSelection() to get the selected items.
var ms = $('#ms-getValue').magicSuggest({
data: [
{
id: 1,
name: 'Paris'
},
{
id: 2,
name: 'New York'
},
{
id: 3,
name: 'Gotham'
}
]
});
function getValues() {
// array
var selected = ms.getSelection();
alert(selected.map(function(item) {
return item.name;
}).join(','));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="http://nicolasbize.com/magicsuggest/lib/magicsuggest/magicsuggest.css" rel="stylesheet" />
<script src="http://nicolasbize.com/magicsuggest/lib/magicsuggest/magicsuggest.js"></script>
<div id="ms-getValue"></div>
<button onclick="getValues()">Get Values</button>
Update The result is an Array. So, to get the first (for example) selected object you do: selected[0].name
Update 2
To show all the selected values separated by comma:
selected.map(function(item) {
return item.name;
}).join(',')
I'm fetching a list of objects from a database, say articles, that have a category attribute, and I'll be adding a filtering capability to my angularjs app where I can select multiple articles based on the subcategories, grouped by category
I'm trying to do as follows in my html:
<select multiple >
<option value="" disabled selected>Choose your option</option>
<optgroup ng-repeat="category in categories" label="{{category.category}}">
<option ng-repeat="subcategory in category.subcategories" value="{{subcategory}}">{{subcategory}}</option>
</optgroup>
</select>
but the categories and subcategories can be diverse and I don't want to hardcode it on my app, rather, grouping that information from all the articles I retrieve from the database, so in my Controller, in the function I use to fetch all the articles I have the following
function getAllArticles(){
var promise = article.getAll();
promise.then(function( articles ){
$scope.articles = articles.data
var result = $scope.articles.map(function(a) {return a.category.category;});
var res = arrayUnique(result);
for(var i = 0; i < res.length; i++){
$scope.categories[i] = {'category': res[i] }
var result2 = $scope.articles.map(function(a) {
if (a.category.category === res[i]) {
return a.category.subcategory;
}
});
$scope.categories[i]['subcategories']
$scope.categories[i]['subcategories'] = arrayUnique(result2);
}
});
}
var arrayUnique = function(a) {
return a.reduce(function(p, c) {
if (p.indexOf(c) < 0 && c != undefined) p.push(c);
return p;
}, []);
};
In a way I'm using map/reduce to get the categories and subcategories, but my problem is that with all of these, in my html, the ng-repeat doesn't show anything, as if the $scope.categories is still empty, even we I console.log it I get the following result:
{ 0: {category: "category1",
subcategories: [{subcategory: "sub1"},{subcategory: "sub2"}]
},
1: {category: "category2",
subcategories: [{subcategory: "sub1"},{subcategory: "sub2"}]
}, ...
}
EDIT:
when I do the following:
<div ng-repeat="category in categories">
{{category.category}}
<div ng-repeat="subcategory in category.subcategories">{{subcategory}}</div>
</div>
It prints as it should the list of categories and subcategories, the main difference is that I'm using <div> instead of <optgroup> <option>
If your console.log is accurate then you are using ng-repeat over an object not an array. This is doable but requires special syntax such as:
<div ng-repeat="(key, value) in myObj"> ... </div>
you can find the documentation here.
Otherwise, try translating the results into an array before using them in ng-repeat.
As a side note, angular offers the ng-options directive for ng-select. That way you can assign a data model to it instead of hard coding a template.
The problem was with materializecss select. It was instantiating before updating $scope.categories, which was empty
The solution can be
$(document).ready(function() {
$('select').material_select();
$('input.select-dropdown').click(function(e){
$('select').material_select();
});
});
solves the problem but it's a horrible hack, but I'll be moving to a timeout solution or instantiating after updating $scope.categories
I try to do a dropdown list in my app. First of all I use a Meteor, so that's specific kind of app ofc :)
Second thing is that I use sebdah/meteor-autocompletion package, because I want my results to be sorted in specific way and limited.
The last thing I need is to group my results.
For example: If I have 2 products named "blah" I want to get only 1 "blag" in my dropdown "autocompletion" list.
Some code:
HTML:
<template name="InvoicesEditInsertInsertForm">
<input id="descriptionautocomplete" type="text" name="description" value="" class="form-control" autofocus="autofocus" placeholder="New Item...">
</template>
JS:
Template.InvoicesEditInsertInsertForm.rendered = function() {
AutoCompletion.init("input#descriptionautocomplete");
};
Template.InvoicesEditInsertInsertForm.events({
'keyup input#descriptionautocomplete': function () {
AutoCompletion.autocomplete({
element: 'input#descriptionautocomplete', // DOM identifier for the element
collection: InvoicesItem, // MeteorJS collection object
field: 'description', // Document field name to search for
limit: 5, // Max number of elements to show
sort: { modifiedAt: -1 },
}); // Sort object to filter results with
},
});
I need to use function that could group my "description" here.
I tried to do it in helper and I get it on my screen, but to be honest I don't know how to put that into my dropdown :(
try: function() {
var item= InvoicesItem.find({},{sort:{modifiedAt:-1}}).fetch();
var descriptions={};
_.each(item,function(row){
var description = row.description;
if(descriptions[description]==null)
descriptions[description]={description:description};
});
return _.values(descriptions);
},
I don't think you can do what you want with that package. If you have a look at the current limitations of the package documentation, you can see other potential solutions to your problem.
You can do addtional filtering as follows:
filter: { 'gender': 'female' }});
but I don't think this will allow you to demand only unique options.
The code you wrote above for try won't do anything. Autocomplete doesn't take a field called try.
Note: I'm not referring to "two way binding"
I'm using a ractive decorator (select2) to transform an input into a select2. The data I obtain through ajax are some records from the database, example:
[{id:1, name:"test", quantity:2, image:"image.jpg"},
{id:2, name:"bar", quantity:21, image:"image2.jpg"},
{id:3, name:"foo", quantity:21, image:"image3.jpg"}]
I format these object using select2's functions, formatResult and formatSelection
The element on which I'm using the decorator is something like this:
<input type="hidden" value="{{values}}" decorator="select2">
After the user select something, values will be equal to the ids of the selected object, (eg: values=1,3 if i select the first and the last records)
My question is: how can i obtain the full object that was selected? I was thinking about two bindings on the <input> (<input value="{{values}}" data-objects="{{objects}}"> so the decorator can save the full objects too, when the user select something. But when i debug the decorator, node._ractive.binding only shows value and not other attributes.
I solved it by saving the result of the ajax request in ractive, then matching the ids with the object ids to find the original objects.
Not the prettiest thing, but it works.
Ractive.decorators.select2.type.whatever = {
tags: [],
separator: "|",
ajax: {
url: "ajax_url",
data: function(searchterm, page) {
return {
searchterm: searchterm,
page: page,
};
},
results: function(data, page) {
//Here i save the records
ractive.set("data", data.records);
return {results: data.records, more: data.more};
}
}
};
var ractive = new Ractive({
el: "things",
template: "template",
});
ractive.observe("ids", function(ids) {
var data = ractive.get("data");
ids = ids.split("|");
//I can obtain the original objects
});
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<script src="https://rawgit.com/Prezent/ractive-decorators-select2/master/ractive-decorators-select2.js"></script>
<!-- select2, jquery missing -->
<script type="ractive-template" id="template">
<input type="hidden" value="{{ids}}" decorator="select:whatever">
</script>
<div id="things"></div>