Meteor: how to retrieve "{{this}}-value" with a template event - javascript

Note: Whole code can be found here:
https://github.com/Julian-Th/crowducate-platform/tree/feature/courseEditRights
The issue: I can't retrieve the {{this}} value with an event. Console.log() is printing 0.
My HTML:
<!-- Modal to control who can collaborate on a course-->
<template name="modalAddCollaborators">
<div id="modalAddCollaborators" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Manage Your Collaborators</h4>
</div>
<div class="modal-body">
<form class="form" role="form">
<ul class="list-group">
{{#each addedCollaborators}}
{{#each canEditCourse}}
<li class="list-group-item js-listed-collaborator">{{this}}<a title="Remove Collaborator" id="remove-collaborator" class="btn btn-danger pull-right" href="#"><i class="fa fa-trash"></i></a></li>
{{/each}}
{{/each}}
</ul>
<div class="form-group">
<input class="form-control typeahead" type="text" id="collaboratorName" placeholder="add a collaborator ..." data-source="courses" autocomplete="off" spellcheck="off">
<button type="button" id="js-addCollaborator" class="btn btn-success">Add</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</template>
My JS:
Template.modalAddCollaborators.rendered = function() {
// initializes all typeahead instances
Meteor.typeahead.inject();
};
Template.modalAddCollaborators.courses = function(){
return Courses.find().fetch().map(function(it){ return it.author; });
//return users.find().fetch().map(function(it){ return it.username; });
};
Template.modalAddCollaborators.helpers({
'addedCollaborators': function () {
return Courses.find().fetch();
}
});
Template.modalAddCollaborators.events({
'click #js-addCollaborator' : function (event) {
var collaboratorName = $('#collaboratorName').val(); //
Courses.update(
{_id: this._id},
{$addToSet: {canEditCourse: collaboratorName}}
);
$('#collaboratorName').val("");
},
'click #remove-collaborator': function (event) {
var listedCollaborator = $('.js-listed-collaborator').val();
console.log(listedCollaborator);
Courses.update(
{_id: this._id },
{$pull: {canEditCourse: listedCollaborator}}
);
}
});
My MongoDB JSON:
{
"_id" : "j7A3tFdFBn5ECQGwe",
"title" : "Beatles",
"coverImageId" : "RERiadyMx8j8C9QQi",
"author" : "John",
"keywords" : [
"Paul"
],
"published" : "true",
"about" : "Testing the Course",
"canEditCourse" : [
"uo8SMdNroPGnxMoRg",
"FLhFJEczF4ak7CxqN",
"lkahdakjshdal",
"asödjaöslkdjalsöSA"
],
"createdById" : "uo8SMdNroPGnxMoRg",
"dateCreated" : ISODate("2015-12-28T16:30:34.714Z")
}
As seen in the JS-File, my final goal is to delete the clicked user from an array.

To get the text of the li item in the child link click event, combine the use of .parent() and .text() (since you can't use .val() on list items):
'click #remove-collaborator': function (event) {
console.log(event.target);
var listedCollaborator = $(event.currentTarget).parent().text();
console.log(listedCollaborator);
console.log(JSON.stringify(Template.parentData(0)));
Courses.update(
{
_id: Template.parentData(0)._id, /* or _id: Template.currentData()._id, */
canEditCourse: listedCollaborator
},
{ $pull: { canEditCourse: listedCollaborator } }
);
}
Notice you can use the current DOM element within the event bubbling phase through event.currentTarget to reference the element that kicked off the event. Since the element is the anchor tag, you get the li item as
its .parent(), and subsequently get its value with .text().
As for the update, use Template.parentData() to get the parent _id. Specify a parameter of 0 in the method which denotes the current data context level to look.
For example, Template.parentData(0) is equivalent to Template.currentData(). Template.parentData(2) is equivalent to {{../..}} in a template.

Since you've attached your event handler to the modalAddCollaborators template this will be the data context of that template which is nothing.
Just setup a nested template at the level you want to catch the event.
Furthermore with this pattern you can identify the _id of the collaborator directly, it will be this. The course _id however comes from the context of the parent template. (I'm not sure whether the course level data context is 1 or 2 levels higher however).
html:
{{#each canEditCourse}}
{{> nestedTemplate }}
{{/each}}
<template name="nestedTemplate">
<li class="list-group-item js-listed-collaborator">
{{this}}<a title="Remove Collaborator" id="remove-collaborator" class="btn btn-danger pull-right" href="#"><i class="fa fa-trash"></i></a>
</li>
</template>
js:
Template.nestedTemplate.events({
'click #remove-collaborator': function (event) {
Courses.update({_id: Template.parentData()._id },{$pull: {canEditCourse: this}});
}
});

Related

Jquery strange chaining of Ajax requests

I have a div called project and it is rendered with EJS
There several projects in the data for EJS, they are rendered by forEach loop - so several similar div appear.
The project div has id for identification in Jquery.
Further it has a project.name and project.id as a data-*
The problem which I encountered:
If I don't reload the page as intended - first try works well and Element inner text get updated correctly.
But on second try to change another project name both are changed to value of previous, so to say for both projects. In few words - new change overrides all previous. How is it possible?
Link to see how it looks in GIF
Imgur
Strange behaviour of chaining requests Imgur
<%userData.forEach(function(project){%>
<div class="project" id='project <%=project.id%>'>
<div class="projectHeader">
<div class="projectTitle">
<h5 id="projectTitle <%=project.id%>" class="projectName">
<%=project.name%>
</h5>
<div class="projectButtons">
<span data-toggle="tooltip" data-placement="top" title="Edit Project Title">
<a data-toggle="modal" data-target="#editProjectTitleModal">
<i id="editProjectName" class="editProject fas fa-pencil-alt"
data-name="<%=project.name%>" data-id="<%=project.id%>"></i>
</a>
</span>
</div>
</div>
</div>
A simple modal is called when the a tag in project is clicked.
<div class="modal fade" id="editProjectTitleModal" tabindex="-1" aria-labelledby="exampleformModal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form class="" action="" method="">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit Title</h5>
</div>
<div class="modal-body">
<div class="input-group">
<input id="editProjectNameInput" autocomplete="off" pattern="[a-zA-Z0-9 ].{1,25}" title="1 to 25 characters" class="form-control" aria-label="With textarea" placeholder="Enter new title" required></input>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" id="confirmEditProjectName" class="btn btn-primary">Save changes</button>
</div>
</form>
</div>
</div>
</div>
Jquery event handler which serves to change project.name, at first sends it to database and ammend DOM with new name. So the database get the new data, but the page is not reloaded and project.name changed simultaneously.
It grabs project-name and project-id and sends Ajax regular post - method, on success - change element's inner text to project-name
// Edit Project Title by ID
$(document).on('click', "#editProjectName", function() {
//Grab Id of the Project
var editProjectId = $(this).attr('data-id');
//Fill Modal input with current project.name
var currentTitle = document.getElementById('projectTitle ' + editProjectId).innerText;
$("#editProjectNameInput").val(currentTitle)
var url = '/editProjectName';
$('#confirmEditProjectName').on('click', function(event) {
//Take new project name from updated modal input
var newTitle = $("#editProjectNameInput").val();
//If they are same - alert
if (currentTitle === newTitle) {
event.preventDefault();
alert("New Title should be different")
} else {
event.preventDefault();
if (newTitle.length > 1 && newTitle.length <= 25) {
$.ajax({
type: "POST",
url: url,
data: {
projectName: newTitle,
projectID: editProjectId
},
success: function(result) {
//Hide modal and change element inner text to new value
$("#editProjectTitleModal").modal('hide')
document.getElementById('projectTitle ' + editProjectId).innerText = newTitle;
},
error: function(err) {
console.log(err);
}
})
}
}
})
})
I removed the space from the IDs and I changed from using the ID of #editProjectName to just using the class that is already on that object of editProject.
<%userData.forEach(function(project){%>
<div class="project" id='project<%=project.id%>'>
<div class="projectHeader">
<div class="projectTitle">
<h5 id="projectTitle<%=project.id%>" class="projectName">
<%=project.name%>
</h5>
<div class="projectButtons">
<span data-toggle="tooltip" data-placement="top" title="Edit Project Title">
<a data-toggle="modal" data-target="#editProjectTitleModal">
<i class="editProject fas fa-pencil-alt"
data-name="<%=project.name%>" data-id="<%=project.id%>"></i>
</a>
</span>
</div>
</div>
</div>
// Edit Project Title by ID
$(document).on('click', ".editProject", function() {
//Grab Id of the Project
var editProjectId = $(this).attr('data-id');
//Fill Modal input with current project.name
var currentTitle = document.getElementById('projectTitle' + editProjectId).innerText;
$("#editProjectNameInput").val(currentTitle)
var url = '/editProjectName';
$('#confirmEditProjectName').on('click', function(event) {
//Take new project name from updated modal input
var newTitle = $("#editProjectNameInput").val();
//If they are same - alert
if (currentTitle === newTitle) {
event.preventDefault();
alert("New Title should be different")
} else {
event.preventDefault();
if (newTitle.length > 1 && newTitle.length <= 25) {
$.ajax({
type: "POST",
url: url,
data: {
projectName: newTitle,
projectID: editProjectId
},
success: function(result) {
//Hide modal and change element inner text to new value
$("#editProjectTitleModal").modal('hide')
document.getElementById('projectTitle' + editProjectId).innerText = newTitle;
},
error: function(err) {
console.log(err);
}
})
}
}
})
})
After some research I have found out that once the on('click') is called it is On until the page get reloaded.
Thanks to this Question and Answer:
https://stackoverflow.com/a/6121501/13541013
I figured out - on('click') event should be switched off by calling $(this).off() (this is the event)
In my case I had to make $(this).off() right after:
$(document).on('click', "#editProjectName", function() {
$(this).off() ... further code
And it has to be done for every single on('click') event in the script.

show particular data in pop up modal in vue.js

this is regarding Vue.js question
i'm trying to open bootstrap model form inside the Vue template
i use two vue template components,
this sub component call inside this competence and pass data from this to sub component
this component use for show particular (load one by one products) model data
so i need to show one by one products data on the model form (when product 1 show name 'Abc') like this
but i cant do this.. all implementation are done and working fine
but cant show the particular data on the model form
show it only first loop value (i have 3 products all load in the table,but when click edit button first product show correctly,but click 2nd product show first product data)
but when i call console.log function and view when open the model show particular data in the console, but not showing its on the model form
why it that
i put my code segment in the below
example-component
<tbody >
<tr div v-for="invoices in invoice">
<th class="invoice_name ">{{invoices.p_name}}</th>
<td class="unit">
<sub-com :pID=invoices.p_id :invoice=invoices :invoiceID=invoice_id></sub-com>
</td>
</tr>
</tbody>
sub-com
<template>
<div>
<div class="form-group">
Refund
</div>
<div class="col-md-6">
<div class="modal fade" id="refundModel" tabindex="-1" role="dialog" aria-labelledby="addNewLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form>
<div class="modal-body">
<div class="form-group">
<input v-model="form.name" type="text" name="name" placeholder="Name" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
this is sub.vue script segment
<script>
export default{
data(){
return{
form: {
name:''
}
}
},
props: {
pID: String,
invoiceID:String,
invoice:{},
}
methods: {
refundMethod(invoices){
this.form.name = invoices.p_name;
console.log(invoices.p_name);
$('#refundModel').modal('show');
}
}
There are a couple of issues that might clear things up.
First you need to add a key to your template v-for loop:
<tr v-for="invoices in invoice" :key="invoices.p_id">
Second you are using jquery to trigger the modal which could work but you will have to generate unique ids for each div:
<div :id="'refundModel_'+pID">
A more Vue way to do this is to use the bootstrap data-show attribute and link it to a Boolean modal property in your data:
<div :data-show="modal" :id="'refundModel_'+pID">
export default {
data(){
return{
modal : false,
form: {
name:''
}
}
},
props: {
pID: String,
invoiceID: String,
invoice: Object,
}
methods: {
refundMethod(invoices){
this.form.name = invoices.p_name;
console.log(invoices.p_name);
this.toggleModal()
}
toggleModal () {
this.modal = !this.modal
}
}
}

Display data when click on button with Meteor

I have one button, when I click I want to display data only if the value of the checkbox is true, If it false, it's display when DOM is created
But I can't please look my code.
Template.students.helpers({
all_students: () => {
return students.find();
}
});
Template.body.onCreated(() => {
Meteor.subscribe('students');
});
Template.students.events({
'submit .insert': (e) => {
e.preventDefault();
students.insert({
name: e.target[0].value,
age: e.target[1].value,
check: false
});
this._checkValue(e);
},
'click .is-delete': (e) => {
students.remove(e.currentTarget.id);
},
'click .check-checkbox': (e) => {
students.update(e.currentTarget.id, {
$set: {
check: !this.check
}
})
},
'click .all': () => {
// HERE
}
})
<template name="students">
<div class="content menu">
<ul>
<button class="ui button all">All list</button> <!-- THIS BUTTON -->
{{#each all_students}}
<li class="content-list" id="{{ _id }}">
<div class="name">{{ name }}</div>
<div class="age">{{ age }} ans</div>
<span id="{{ _id }}" class="delete is-delete"></span>
<div class="ui checkbox">
<input id="{{ _id }}" class="check-checkbox" type="checkbox" name="check">
</div>
</li>
{{/each}}
</ul>
</div>
</template>
Inside of my event handler click .all if I try to return students.find() it doesn't work.
The easiest way is to use a ReactiveVar to flag if the list should show like so:
Add the ReactiveVar to your template instance
Template.students.onCreated(() => {
this.showAllStudents = new ReactiveVar(false);
this.subscribe('students');
});
Then expose it with a helper:
Template.students.helpers({
showStudents() {
Template.instance().showAllStudents.get();
},
all_students() {
students.find();
};
});
In your template, test for the flag
<template name="students">
<div class="content menu">
<ul>
<button class="ui button all">All list</button> <!-- THIS BUTTON -->
{{#if showStudents}}
{{#each all_students}}
<li class="content-list" id="{{ _id }}">
<div class="name">{{ name }}</div>
<div class="age">{{ age }} ans</div>
<span id="{{ _id }}" class="delete is-delete"></span>
<div class="ui checkbox">
<input id="{{ _id }}" class="check-checkbox" type="checkbox" name="check">
</div>
</li>
{{/each}}
{{/if}}
</ul>
</div>
</template>
And add the event handler which just switches the state (ie. set opposite of current state):
Template.students.events({
'click .all': (event, instance) => {
instance.showAllStudents.set(!instance.showAllStudents.get());
}
})
If you haven't already got it, run meteor add reactive-var to get the package.
And if you're using imports, use import { ReactiveVar } from 'meteor/reactive-var'; to import it.

Knockout : Unable to process binding on observable & issue with initialization

I have googled around, and tried to fix this as good as i can with examples i have found around, but alas... no success.
Mission :
Modal is opened and displaying checkbox for selecting an already existing user
If clicked -> Dropdown visible with available persons to select from
Source of dropdown (select) works as it should..
When person is selected from dropdown, a api-call (not implemented yet) will return an object to fill newOrExistingPlayer observable, and displaying it's data in fields..
If no person selected from dropdown, it's a new registration without pre-selecting a person.
Error :
knockout-3.4.0.debug.js:3326 Uncaught ReferenceError: Unable to process binding "with: function (){return newOrExistingPlayer }"
Message: Unable to process binding "value: function (){return selectedPersonId }"
Message: selectedPersonId is not defined
Problem :
Before a person is selected, newOrExistingPlayer is "undefined". Therefore i made a "teamPlayerDefault" js-object with the data similar to what should be returned from the api call (not implemented yet).
This is for initializing..
I don't think i'm handling empty observables the correct way. Should they be initialized in some way to avoid this ?
JSFiddle Link :
Click here...
Code :
$(document).ready(function() {
var NewTeamPlayerViewModel = function() {
var teamPlayerDefault = {
Id: 0,
ExistingPersonId: 0,
Email: "",
Email2: "",
FirstName: "",
LastName: "",
Address: "",
PostalCode: "",
PostalCity: "",
Phone: "",
Phone2: "",
BirthdayString: "",
ShirtNo: 0,
TeamIdString: getQueryVariable("teamId")
};
var self = this;
self.existingPersonChecked = ko.observable(false);
self.existingPersons = ko.observableArray();
self.selectedPersonId = ko.observable(null);
self.selectedPersonId.subscribe(function(selPersonId) {
// Handle a change here, e.g. update something on the server with Ajax.
console.log('Valgt personid ' + selPersonId);
});
self.newOrExistingPlayer = ko.observable(teamPlayerDefault);
self.setExistingPlayer = function(personId) {
// TODO : GET EXISTING PLAYER
self.newOrExistingPlayer(null);
console.log(self.newOrExistingPlayer());
}
self.toggleExistingPersonChecked = function() {
self.existingPersonChecked(!self.existingPersonChecked);
}
// TODO UGLE : Ikke hent alle personer, men ekskluder de som allerede er spillere på laget!!!
self.initializeFromServer = function() {
//var teamId = getQueryVariable("teamId");
var url = 'api/User/GetAllPersons';
$.getJSON(url)
.done(function(data) {
newPlayerModel.existingPersons(data);
//console.table(data);
});
}
}
var newPlayerModel = new NewTeamPlayerViewModel();
newPlayerModel.initializeFromServer();
ko.applyBindings(newPlayerModel, document.getElementById("ko-player"));
console.log("Heisann!" + newPlayerModel.newOrExistingPlayer());
});
<div id="ko-player">
<div class="modal fade" data-bind="with: newOrExistingPlayer" id="full-modal-player" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="z-index: 999999999999">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 id="myModalLabel">Ny spiller</h4>
</div>
<div class="modal-body" style="height: 100% !important; max-width: 100%; height:800px">
<div class="row">
<div class="col-sm-12">
<div class="col-sm-6">
<div class="checkbox">
<label class="checkbox-label">Velg eksisterende person?</label>
<input type="checkbox" data-bind="checked: $parent.existingPersonChecked, click: $parent.toggleExistingPersonChecked" />
</div>
</div>
<div class="col-sm-6" style="display: none" data-bind="visible: $parent.existingPersonChecked">
<div class="form-group">
<label>Velg person:</label>
<select data-bind="options: $parent.existingPersons, value: selectedPersonId, optionsCaption: 'Velg en person'"></select>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Lukk</button>
<div class="clear:both; height:1px"> </div>
</div>
</div>
</div>
</div>
</div>
You likely have figured this out by now but I was able to get it binding with -
<select data-bind="value: $parent.selectedPersonId"></select>
Just to expand on why this is, you are binding 'with' newOrExistingPlayer and need to step up a level to access where you defined the selectedPersonId

Why is extending an extended view not working in ember.js?

I am trying to create a modal view and have a base class that all modals need and then extending it for more specific functionality.
PlanSource.Modal = Ember.View.extend({
isShowing: false,
hide: function() {
this.set("isShowing", false);
},
close: function() {
this.set("isShowing", false);
},
show: function() {
this.set("isShowing", true);
}
});
PlanSource.AddJobModal = PlanSource.Modal.extend({
templateName: "modals/add_job",
createJob: function() {
var container = $("#new-job-name"),
name = container.val();
if (!name || name == "") return;
var job = PlanSource.Job.createRecord({
"name": name
});
job.save();
container.val("");
this.send("hide");
}
});
I render it with
{{view PlanSource.AddJobModal}}
And have the view template
<a class="button button-green" {{action show target=view}}>+ Add Job</a>
{{#if view.isShowing}}
<div class="modal-wrapper">
<div class="overlay"></div>
<div class="dialog box box-border">
<div class="header">
<p class="title">Enter a job name.</p>
</div>
<div class="body">
<p>Enter a name for your new job.</p>
<input type="text" id="new-job-name" placeholder="Job name">
</div>
<div class="footer">
<div class="buttons">
<a class="button button-blue" {{action createJob target=view}} >Create</a>
<a class="button" {{action close target=view}}>No</a>
</div>
</div>
</div>
</div>
{{/if}}
The problem is that when I click the button on the modal dialog, it gives me an "action createJob" can not be found. Am I extending the objects incorrectly because it works if I put the createJob in the base Modal class.
Fixed
There was an issue somewhere else in my code. The name got copied and so it was redefining it and making the method not exist.

Categories