Prevent inserting duplicate elements - instead routing to existing element - javascript

This is how I'm adding some elements to a list (which consists of links to articles) via an input field:
Template.addForm.events({
'submit form': function(event){
event.preventDefault();
var title = event.target.text.value;
MongoValues.insert({
title: title,
slug: title.toLowerCase()
}, function(error, result) { if(error) console.warn(error); });
event.target.text.value = "";
}
});
Now I want to prevent double entries: If the user wants to add an already existing title he should be routed to this already existing element (route to article/_id), instead of adding the title to the list.

Assuming you are using iron:router and have a route like this :
Router.route('article/:_id', {
name: 'article'
// other route stuff
});
You could adjust your code as follows:
Template.addForm.events({
'submit form': function(event){
event.preventDefault();
var title = event.target.text.value;
var existing = MongoValues.findOne({title : title});
if (!!existing) {
// title already exists, go to article page
Router.go("article", {_id : existing._id});
} else {
// title doesnt exist, so go ahead and insert
MongoValues.insert({
title: title,
slug: title.toLowerCase()
}, function(error, result) {
if(error) {
console.warn(error);
}
});
event.target.text.value = "";
}
}
});
Note that this will not prevent duplicates if the user bypasses this code (i.e. by doing the insert from the console).
If you are using Collection2 and SimpleSchema, you can set a unique constraint on the title field to ensure that only unique values ever get inserted, regardless of where the insert happens. To do this, just specify "unique: true" in your field definition, like so:
title : {
type: String,
unique: true
}

Related

Associate Lists and Tasks in Meteor todo

I'm building the todo application from the Meteor tutorial and continue it. I'm building some lists based on the task model, but I don't know how to join them and say when I click on one list, I want all the tasks from this one.
For the moment, I have the Tasks.js with:
'tasks.insert'(text, privacy, priority, listId) {
...
Tasks.insert({
text,
listId: listId,
owner: this.userId,
username: Meteor.users.findOne(this.userId).username,
});
},
Body.js
Template.body.events({
'submit .new-task' (event) {
event.preventDefault();
const listId = ???
const target = event.target;
const text = target.text.value;
...
Meteor.call('tasks.insert', text, privacy, priority, listId);
...
},
And then where I display it:
Template.body.helpers({
tasks() {
const instance = Template.instance();
if (instance.state.get('hideCompleted')) {
return Tasks.find({ checked: { $ne: true } }, { sort: Session.get("sort_order") });
}
return Tasks.find({}, { sort: Session.get("sort_order")});
},
lists() {
return Lists.find({}, { sort: { createdAt: -1 } });
},
I my body.html, I just display each items (lists and tasks) separately. But the problem is I don't know how to make the relation between both ...
Can you help me please ?
Thanks a lot
I see you are already using Session. Basically, you will use a Session variable that tracks what the list the user has selected, and then filter your tasks with that variable.
In your body, where you're displaying your list names, add the list's id as an HTML attribute:
{{#each lists}}
<a href='#' class='list-name' data-id='{{this._id}}'>
{{this.name}}
</a>
{{/each}}
Add an event for clicking on a list name that saves its id to a Session variable:
Template.body.events({
'click .list-name' (event) {
event.preventDefault();
Session.set('listId', event.currentTarget.attr('data-id'))
}
})
In your tasks helper, filter your query using the Session variable:
return Tasks.find(
{ listId: Session.get('listId') },
{ sort: Session.get("sort_order") }
);
Let me know if anything could be more clear.

Update array order in Vue.js after DOM change

I have a basic Vue.js object:
var playlist = new Vue({
el : '#playlist',
data : {
entries : [
{ title : 'Oh No' },
{ title : 'Let it Out' },
{ title : 'That\'s Right' },
{ title : 'Jump on Stage' },
{ title : 'This is the Remix' }
]
}
});
HTML:
<div id="playlist">
<div v-for="entry in entries">
{{ entry.title }}
</div>
</div>
I also am using a drag and drop library (dragula) to allow users to rearrange the #playlist div.
However, after a user rearranges the playlist using dragula, this change is not reflected in Vue's playlist.entries, only in the DOM.
I have hooked into dragula events to determine the starting index and ending index of the moved element. What is the correct way to go about updating the Vue object to reflect the new order?
Fiddle: https://jsfiddle.net/cxx77kco/5/
Vue's v-for does not track modifications to the DOM elements it creates. So, you need to update the model when dragula notifies you of the change. Here's a working fiddle: https://jsfiddle.net/hsnvweov/
var playlist = new Vue({
el : '#playlist',
data : {
entries : [
{ title : 'Oh No' },
{ title : 'Let it Out' },
{ title : 'That\'s Right' },
{ title : 'Jump on Stage' },
{ title : 'This is the Remix' }
]
},
ready: function() {
var self = this;
var from = null;
var drake = dragula([document.querySelector('#playlist')]);
drake.on('drag', function(element, source) {
var index = [].indexOf.call(element.parentNode.children, element);
console.log('drag from', index, element, source);
from = index;
})
drake.on('drop', function(element, target, source, sibling) {
var index = [].indexOf.call(element.parentNode.children, element)
console.log('drop to', index, element, target, source, sibling);
self.entries.splice(index, 0, self.entries.splice(from, 1)[0]);
console.log('Vue thinks order is:', playlist.entries.map(e => e.title ).join(', ')
);
})
}
});
I created a Vue directive that does exactly this job.
It works exactly as v-for directive and add drag-and-drop capability in sync with underlying viewmodel array:
Syntaxe:
<div v-dragable-for="element in list">{{element.name}}</div>
Example: fiddle1, fiddle2
Github repository: Vue.Dragable.For

Select2 inserts an empty text option in the dynamic list

I am using Select2 with Jquery-editable and encountering an abnormal behavior of Select2, what I am doing is displaying editable table of information using ejs template, and as user clicks on CBA opens up a select2 box which have the originally selected result, and then user can add or delete options in it, options comes from Database source, and when user selects an options it adds an empty option in database with the selected option , the array looks like this
[ "ABCD", "ONAB", "" , "BCNU" ]
I read somewhere about allowClear: true and add a placeHolder but It doesn't helped me at all. As everything is done dynamically I can't find where that empty option is added.
Code is below:
Ejs/HTML code for Select 2
<tr>
<td width="40%">Select CBA(s)</td>
<td>
<a class="cbaSelectUnit" data-emptytext="Select CBA(s)" data-original-title="Select CBA(s)" data-type="select2"></a>
</td>
Javascript for Select 2
$("a[data-name='Cba']").editable({
showbuttons: 'false',
emptytext: 'None',
display: function(values) {
var html = [];
html.push(values);
$(this).html(html);
},
select2: {
multiple: true,
allowClear: true,
placeholder: "Select CBA(s)",
ajax: {
// url is copied from data-source via x-editable option-passing mechanism
dataType: 'json',
// pass the '?format=select2' parameter to API call for the select2-specific format
data: function(term, page) {
return {
deptId: departmentId,
format: 'select2'
};
},
// transform returned results into the format used by select2
results: function(data, page) {
return {
results: data
};
}
},
// what is shown in the list
formatResult: function(cba) {
return cba.text;
},
// what will appear in the selected tag box
formatSelection: function(cba) {
return cba.text;
},
// rendering id of the values to data.value requirement for Select 2
id: function(cba) {
return cba.value;
},
// what is shown in the selected-tags box
initSelection: function(element, callback) {
var id = $(element).val(),
result = id.replace(/^,\s*$/, ',').split(",").map(function(v) {
return {
id: v,
text: v
};
});
callback(result);
}
}
});
Format in which Code is returned from the database:-
Facility.findOne({ _id: department.Facility }, function(err, facility) {
if (err) {
res.send(500, err);
} else if (!facility) {
res.send(404, 'Facility not found');
} else if (req.query.format && req.query.format === 'select2') {
var result = facility.Cba.map(function(c) {
return { value: c, text: c };
});
res.json(result);
}
});
Image showing an empty box added by itself
How Array looks after I edit
So it was just a simple syntax error, I was doing found out by myself,
I was returning cba.value as id, but the initSelection was returning
{id: v, text: v}
it should be value & text instead of id & text.
// what is shown in the selected-tags box
initSelection: function(element, callback) {
var id = $(element).val(),
result = id.replace(/^,\s*$/, ',').split(",").map(function(v) {
return {
value: v,
text: v
};
});
callback(result);
}

jTable Conditional show\hide edit and delete buttons based on owner of data

Im using jTable to display CDs info and a child table to show reviews of that CD. I want to be able to only show the edit\delete buttons on the rows for the user that is logged in. I have been trying to follow the suggestions made on: https://github.com/hikalkan/jtable/issues/113
https://github.com/hikalkan/jtable/issues/893
https://github.com/hikalkan/jtable/issues/620
Can honestly say im not having much luck with any of these examples. We had been told to include some jquery in our assignment so I chose to go with using it for my table data. Im wishing now id just done something very basic!
Working jTable without condition:
display: function (reviewData) {
//Create an image that will be used to open child table
var $img = $('<img class="child-opener-image" src="/Content/images/Misc/list_metro.png" title="List Reviews" />');
//Open child table when user clicks the image
$img.click(function () {
$('#ReviewTableContainer').jtable('openChildTable',
$img.closest('tr'),
{
title: "Your reviews on this album",
actions: {
listAction: 'childReviewActions.php?action=list&ID=' + reviewData.record.CDID,
deleteAction: 'childReviewActions.php?action=delete&ID=' + reviewData.record.CDID,
updateAction: 'childReviewActions.php?action=update&ID=' + reviewData.record.CDID
},
fields: {
userID: {
key: true,
create: false,
edit: false,
list: false
},
userName: {
title: 'User',
edit: false,
width: '20%'
},
reviewDate: {
title: 'Review date',
width: '20%',
type: 'date',
edit: false,
displayFormat: 'dd-mm-yy'
},
reviewText: {
title: 'Review',
type: 'textarea',
width: '40%'
}
},
Issue 620 attempt:
actions: {
listAction: 'childReviewActions.php?action=list&ID=' + reviewData.record.CDID,
#if (reviewData.record.userID == <?php echo mysql_real_escape_string($_SESSION['ID']);?>)
{
deleteAction: 'childReviewActions.php?action=delete&ID=' + reviewData.record.CDID,
updateAction: 'childReviewActions.php?action=update&ID=' + reviewData.record.CDID
}
},
This way gives me compile error: invalid property id on the IF statement.
If I take out the # in the if statement I get: missing : after property id.
Issue 113 & 893 attempt:
actions: {
listAction: {
url:'http://localhost/childReviewActions.php?action=list&ID=' + reviewData.record.CDID
//updateAction: {
//url:'childReviewActions.php?action=update&ID=' + reviewData.record.CDID,
//enabled: function (data) {
//return data.record.userID = <?php echo mysql_real_escape_string($_SESSION['ID']);?>;
//}
//}
},
On this I couldnt even get it to list the contents of the child table. It keeps coming back with 404 not found error: The requested url /[object object] was not found on this server. Has anyone any ideas how to get these examples working on have a different example of how to get the table to enable\enable the edit, update buttons? This is all new to me so I apologise now
rowInserted: function (event, data) {
//After child row loads. Check if the review belongs to the member logged in. If not remove the edit/delete buttons
if (data.record.userID != $user) {
data.row.find('.jtable-edit-command-button').hide();
data.row.find('.jtable-delete-command-button').hide();
}
else{
//If a review record does belong to the user set variable to true so the add new review link can be hidden after all records have been loaded
$memberReviewExists = true;
//Also needed here for when a new record is inserted
$(".jtable-add-record").hide();
}
},
recordsLoaded: function (event, data) {
if (typeof $memberReviewExists != 'undefined' && $memberReviewExists == true){
$(".jtable-add-record").hide();
$memberReviewExists = null;
}
else {
//No review currently exists for this user so show the Add review link $(".jtable-add-record").show();
}
},
recordDeleted: function (event, data) {
//User has deleted their review. Re-show the add new review link
$(".jtable-add-record").show();
}
The following worked for me. It hides the edit/delete button on rows where the current user is not the authorized user. Note: I added a column for authorizedUser in the mysql table and use that to know if the user is allowed or not.
rowInserted: function(event, data){
var $currentUser='<?php echo $_SESSION['email']?>';
if (data.record.authorizedUser != $currentUser) {
data.row.find('.jtable-edit-command-button').hide();
data.row.find('.jtable-delete-command-button').hide();
}
},
#Toni Your code contains asp.net code too. # is ASP.NET Directive.

Extjs - Dynamically generate fields in a FormPanel

I've got a script that generates a form panel:
var form = new Ext.FormPanel({
id: 'form-exploit-zombie-' + zombie_ip,
formId: 'form-exploit-zombie-' + zombie_ip,
border: false,
labelWidth: 75,
formBind: true,
defaultType: 'textfield',
url: '/ui/modules/exploit/new',
autoHeight: true,
buttons: [{
text: 'Execute exploit',
handler: function () {
var form = Ext.getCmp('form-exploit-zombie-' + zombie_ip);
form.getForm().submit({
waitMsg: 'Running exploit ...',
success: function () {
Ext.beef.msg('Yeh!', 'Exploit sent to the zombie.')
},
failure: function () {
Ext.beef.msg('Ehhh!', 'An error occured while trying to send the exploit.')
}
});
}
}]
});
that same scripts then retrieves a json file from my server which defines how many input fields that form should contain. The script then adds those fields to the form:
Ext.each(inputs, function(input) {
var input_name;
var input_type = 'TextField';
var input_definition = new Array();
if(typeof input == 'string') {
input_name = input;
var field = new Ext.form.TextField({
id: 'form-zombie-'+zombie_ip+'-field-'+input_name,
fieldLabel: input_name,
name: 'txt_'+input_name,
width: 175,
allowBlank:false
});
form.add(field);
}
else if(typeof input == 'object') {
//input_name = array_key(input);
for(definition in input) {
if(typeof definition == 'string') {
}
}
} else {
return;
}
});
Finally, the form is added to the appropriate panel in my interface:
panel.add(form);
panel.doLayout();
The problem I have is: when I submit the form by clicking on the button, the http request sent to my server does not contain the fields added to the form. In other words, I'm not posting those fields to the server.
Anyone knows why and how I could fix that?
Your problem is here:
id: 'form-exploit-zombie-'+zombie_ip,
formId: 'form-exploit-zombie-'+zombie_ip,
what you are doing is that you are setting the id attribute of the form panel and the id attribute of the form (form tag) to the same value. Which means that you have two elements with the same id and that is wrong.
Just remove this line
formId: 'form-exploit-zombie-'+zombie_ip,
and you should be fine.
Did you check the HTTP Request parameter for the form values?
If you server side is in PHP, what do you get from response by passing any field name? For example, if one of your input name was "xyz" what do you get by
$_POST[ 'txt_xyz' ]

Categories

Resources