Remove Item from Array in Meteor.js - javascript

I have a collection called Rulesets - each ruleset has an array of "rules". I have the following html which displays each ruleset and each rule:
<template name="rulesets">
{{#each rulesets}}
{{>rulesetSingle}}
{{/each}}
</template>
<template name="rulesetSingle">
{{#each rules}}
<p class="rule-description">{{this}}
x
</p>
{{/each}}
</template>
I want to be able to remove the rule when the user clicks the "rule-delete-btn".
I have the following javascript to do this:
Template.rulesetSingle.event({
'click .rule-delete-btn': function(e){
e.preventDefault();
var array = Rulesets.rules;
var index = array.indexOf(this);
array.splice(index, 1);
}
});
The delete isn't working because the "array" declaration isn't pulling a valid array. How can I find and store the array that contains "this" which is the current rule that needs to be deleted?

Assuming your Rulesets.rules array is an array of objects like :
Rulesets : { { title: 'title1',
rules : [ {name : 'rule1', description : 'description1'},
{name : 'rule2', description : 'description2'}
]
},
{ title: 'title2',
rules : [ {name : 'rule1', description : 'description1'},
{name : 'rule2', description : 'description2'}
]
} }
First, in your rulesetSingle template, you must assign the _id of that particular document to the <a> like :
x
Second thing, you are trying to remove rule entry using Array.splice(), this is not possible since the rules is a document inside the collection. You must do update query as Rulesets.update().
If you have done allow update on the server, you can delete this array entry from within the event handler, otherwise you must do Meteor.call() by passing rule an the _id of parent document.
So, the event handler will look something like :
'click .rule-delete-btn': function(e) {
e.preventDefault();
var rule = this;
var id = e.currentTarget.name;
Meteor.call('removeRule', id, rule);
}
On the server:
Meteor.methods({
removeRule: function(id, rule){
Rulesets.update({_id: id}, {$pull : {rules : rule}});
}
});

Related

changing model property value is changing the same property in other object

im new to vue , i want user to be able to add some specific social media links to the page and edit some properties like it's text
i have 2 objects in my data , models and defaults
defaults contains selectable option for social media links and their initial values
basically i copy the default value into models and let the user customize the model via inputs
data () {
return {
models : [] ,
defaults : {
twitter : { id : null , placeholder : 'my twitter' , icon : 'twitter' , text : null , 'link' : null } ,
instagram : { id : null , placeholder : 'my instagram' , icon : 'instagram' , text : null , 'link' : null } ,
tiktok : { id : null , placeholder : 'my tiktok' , icon : 'tiktok' , text : null , 'link' : null } ,
} ,
}
} ,
so there a select menu for user to select which social he wants to add to the page
Select :
<ul >
<li v-for="(social, index ) in defaults" :key="index">
<a #click="appendSocial(index)">
{{ index }}
</a>
</li>
</ul>
here is my #click="appendSocial(index)" function
appendSocial(type){
let typedefault = this.defaults[type];
this.models.push(typedefault)
},
and finally i show my models to user and provide an input for editing it's text via v-model
<div v-for="(model, index) in models" v-bind:key="model.id">
<a class="button-preview">
{{ model.text === null ? model.placeholder : model.text }}
</a>
<label>Text</label>
<input type="text" v-model="model.text" :key="index" :placeholder="model.placeholder">
</div>
so here is the problem , for some reason changing the models properties will change the same property in defaults ... and changing defaults properties will change the same property models !!
like if i add a twitter menu to the page and change it's text (model.text) to abc via v-model ... it will also change defaults.twitter.text to abc
to better demonstrate the problem i've added some console log to my appendSocialfunction
appendSocial(type){
let typedefault = this.defaults[type];
console.log(`-------- default object for ${type} -----------`);
console.log(typedefault);
this.addElement(typedefault);
},
here is the result
1 - i've selected a twitter link and added to my models , you can see defaults.twitter.text is null
2 - i've change my model.text (i suppose it would be models[0].text) to abc
3 - i've added another twitter link to my page ... this time defaults.twitter.text is also abc
also changing defaults properties will effect all the models the has been getting their value from that default object
like if i change the defaults.instagram.text to xyz all my models which have got their initial values from defaults.instagram will also change their text to xyz
it's like they are referencing each other , but i haven't passed the value between 2 objects by reference
im not sure what's happening here and how can i prevent this ?
This is because
let typedefault = this.defaults[type];
this.models.push(typedefault)
Is storing the reference to the object into your this.models array. And so if you mutate the element, you're by default changing the base object. A quick and dirty way of doing a deep clone is the following.
let typedefault = this.defaults[type];
let clonedObj = JSON.parse(JSON.stringify(typedefault));
this.models.push(clonedObj)
Note: Lodash library does have a proper deep clone functionality.
https://www.geeksforgeeks.org/lodash-_-clonedeep-method/

Overriding combo box for filtering items into the combobox, how to use this.filter item

I am using UI5 framework v1.38.39 and I want to do a custom combobox (no sap.m.Combobox but custom.comboboxsearch (which include sap.m.combobox and sap.m.comboboxbase)) where I can search what is inside (searching by contain) from key and text so I have something like follow in the custom control:
sap.ui.define([
"sap/m/ComboBox",
"sap/m/ComboBoxBase"
],
function (oComboBox, oComboBoxBase) {
"use strict";
/*some stuff*/
sValue = oEvent.target.value;
aVisibleItems1 = this.filterItems({
property: "text",
value: sValue
});
aVisibleItems2 = this.filterItems({
property: "key",
value: sValue
});
/*some stuff*/
My problem is that I don't have 2 filter, the second simply replace the first one then I don't have the items from the property text only from key, I would like to have both then I tried :
sValue = oEvent.target.value;
aVisibleItems1 = this.filterItems({
property: ["text","key"],
value: sValue
});
which give me the error :
Uncaught TypeError: p.charAt is not a function
at f.d.filterItems (ComboBox.js:6)
ComBox.js is a core file of UI5, then I tried :
sValue = oEvent.target.value;
aVisibleItems1 = this.filterItems({
property: "key" || "text",
value: sValue
});
Which also didn't work because take in consideration only key and no text, does someone has a solution ?
PS: As I use dynamic binding I suppose I cannot include items in view they are bind via another controller.
the view is :
<!--some stuff-->
<Label text="{i18n>MyText}" />
<Custom:ComboBoxSearch id="mycustombox" selectionChange='onChange'>
<core:Item key="{key}" text="{text}" />
</Custom:ComboBoxSearch>
<!--some stuff-->
items are added from the controller
mhm, i dont know if i understand your question right but i think it is this what you want.
<ComboBox
id="combobox1"
showSecondaryValues= "true"
items="{
path: '/yourdata',
sorter: { path: 'text' }
}">
<core:ListItem key="{key}" text="{text}" additionalText = "{key}"/>
</ComboBox>
and in your controller it should look like this:
this.getView().byId("combobox1").setFilterFunction(function(sTerm, oItem) {
return oItem.getText().match(new RegExp(sTerm, "i")) || oItem.getKey().match(new RegExp(sTerm, "i"));
});
so you can search for the key or the text it does not matter
sap.ui.ComboBox has a property called filterSecondaryValues which filters both the text and additionalText.
If you set 'additionalText' of the ListItem aggregation to the value of key (let showSecondaryValiues false if you dont wanna show them) and it should work?
<ComboBox
showSecondaryValues= "false"
filterSecondaryValues="true"
items="{
path: '/CountriesCollection'
}">
<core:ListItem key="{key}" text="{text}" additionalText="{key}"/>
</ComboBox>

AngularJS - Get printed value from scope inside an attribute?

I'm currently working on an AngularJS project and I got stuck in this specific requirement.
We have a service that has all the data, DataFactoryService. Then, I have a controller called DataFactoryController that is making the magic and then plot it in the view.
<div ng-repeat = "list in collection">
{{list.name}}
...
</div>
Now, we have a requirement that pass multiple data into one element. I thought an "ng-repeat" would do, but we need to have it inside an element attribute.
The scenarios are:
At one of the pages, we have multiple lists with multiple data.
Each data has a unique code or ID that should be passed when we do an execution or button click.
There are instances that we're passing multiple data.
Something like this (if we have 3 items in a list or lists, so we're passing the 3 item codes of the list):
<a href = "#" class = "btn btn-primary" data-factory = "code1;code2;code3;">
Submit
</a>
<a href = "#" class = "btn btn-default" data-factory = "code1;code2;code3;">
Cancel
</a>
In the example above, code1,code2,code3 came from the list data. I tried several approach like "ng-repeat", "angular.each", array, "ng-model" but I got no success.
From all I've tried, I knew that "ng-model" is the most possible way to resolve my problem but I didn't know where to start. the code below didn't work though.
<span ng-model = "dataFactorySet.code">{{list.code}}</span>
{{dataFactorySet.code}}
The data is coming from the service, then being called in the controller, and being plot on the HTML page.
// Controller
$scope.list = dataFactoryService.getAllServices();
The data on the list are being loaded upon initialization and hoping to have the data tags initialized as well together with the list data.
The unique code(s) is/are part of the $scope.list.
// Sample JSON structure
[
{ // list level
name: 'My Docs',
debug: false,
contents: [ // list contents level
{
code: 'AHDV3128',
text: 'Directory of documents',
...
},
{
code: 'AHDV3155',
text: 'Directory of pictures',
...
},
],
....
},
{ // list level
name: 'My Features',
debug: false,
contents: [ // list contents level
{
code: 'AHGE5161',
text: 'Directory of documents',
...
},
{
code: 'AHGE1727',
text: 'Directory of pictures',
...
},
],
....
}
]
How can I do this?
PLUNKER -> http://plnkr.co/edit/Hb6bNi7hHbcFa9RtoaMU?p=preview
The solution for this particular problem could be writing 2 functions which will return the baseId and code with respect to the list in loop.
I would suggest to do it like below
Submit
Cancel
//inside your controller write the methods -
$scope.getDataFactory = function(list){
var factory = list.map( (a) => a.code );
factory = factory.join(";");
return factory;
}
$scope.getDataBase= function(list){
var base= list.map( (a) => a.baseId);
base= base.join(";");
return base;
}
Let me know if you see any issue in doing this. This will definitely solve your problem.
You don't really have to pass multiple data from UI if you are using Angular.
Two-way data binding is like blessing which is provided by Angular.
check your updated plunker here [http://plnkr.co/edit/mTzAIiMmiVzQfSkHGgoU?p=preview]1
What I have done here :
I assumed that there must be some unique id (I added Id in the list) in the list.
Pass that Id on click (ng-click) of Submit button.
You already have list in your controller and got the Id which item has been clicked, so you can easily fetch all the data of that Id from the list.
Hope this will help you... cheers.
So basing from Ashvin777's post. I came up with this solution in the Controller.
$scope.getFactoryData = function(list) {
var listData = list.contents;
listData = listData.map(function(i,j) {
return i.code;
});
return listData.join(';');
}

Group results in autocompleted dropdown [Meteor]

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.

Jquery - Array manipulation from a select box

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.

Categories