JS function undefined when calling from certain click events in handlebars template - javascript

I am not understanding why the function goToProfile(otherUserId) is 'not defined' only some of the times.
For example, on the page, I have a link to a user that works fine by using the '.userProfileLink' click event.
<%= link_to image_tag(user.avatar.url(:medium)), "/profile/#{user.id}/view", class: "userProfileLink", :"value" => user.id, remote: true %>
Then I click #requestMeeting which renders this handlebars template:
<script id="request-meeting-form" type="text/x-handlebars-template">
<form id="requestMeetingForm">
<div class="form_section">
<h4 style="text-align:center;">Request 1 Hour Meeting</h4>
<br>
<div class="wrapper_input col_8_of_16">
<h4 class="label_standard">Date</h4>
<input type="text" id="meeting_date" class="input_standard datePicker" onfocus="this.blur()"></input>
<input type="hidden" id="alt_meeting_date" class="input_standard datePicker"></input>
</div>
<div class="wrapper_input col_4_of_16">
<h4 class="label_standard">Start Time</h4>
<input type="text" id="meeting_time" class="input_standard time_picker" onfocus="this.blur()"></input>
</div>
</div>
<div class="form_section">
<div class="wrapper_input">
<use xlink:href="#map"/></use></svg>
</div>
<br>
<div class="wrapper_input col_8_of_16">
<input type="text" name="location" id="locationField" placeholder="Location Name" class="input_standard" ></input>
</div>{{!--
--}}<div class="wrapper_input col_8_of_16">
<input type="text" name="location_address" id="addressField" placeholder="Location Address" class="input_standard"></input>
</div>
<input type="hidden" id="currentUser"></input>
</div>
<div id="mapLocation">
</div>
**************** IMPORTANT PART *********************
<div class="form_section submit_cancel">
<div class="wrapper_input cancel" >
<use xlink:href="#circleClose"/></svg>
</div>
********************************************
<div class="wrapper_input submit">
<div class="actions">
<button type="submit" class="btn_primary" id="requestMeetingButton" >Request Meeting <svg class="" ><use xlink:href="#sendPlane"/></svg></button>
</div>
</div>
</div>
</form>
</script>
When I try to call goToProfile again in the template above, I get an error that goToProfile is not defined.
application.js:
$(document).on('click', '#goToProfile', function(e) {
e.preventDefault();
var value = $(this).attr('value')
var otherUserId = parseInt(value);
$('#profileSection').empty();
goToProfile(otherUserId);
});
var ready;
ready = function(){
var currentUserId;
getCurrentUser().done(function() {
var currentUserId = currentUserId
});
$('.userProfileLink').click(function(e) {
var value = $(this).attr('value')
otherUserId = value;
goToProfile(otherUserId);
});
var profileSource = $("#user-profile").html();
var compiledProfile = Handlebars.compile(profileSource);
var languageSource = $("#languages").html();
var compiledLanguage = Handlebars.compile(languageSource);
var language = ""
var currentUserId;
var goToProfile = function(otherUserId) {
$.ajax({
url: '/get_user',
type: 'GET',
data: {otherUserId: otherUserId},
success: function(user) {
var profileInfo;
getUserImage(otherUserId).done(function() {
var profileInfo = {
first_name: user.first_name,
location: user.location,
bio: user.bio,
nationality: user.nationality,
userId: user.id,
userImage: userImage,
};
var profileTemplate = compiledProfile(profileInfo);
$('.yieldedInfo').empty();
$('.yieldedInfo').append('<div class="profileContainer">' + profileTemplate + '</div>');
});
getLanguagesUsers(user.id);
$('#requestMeeting').click(function(e) {
e.preventDefault();
requestMeetingForm(this.value);
});
},
error: function(err) {
console.log(err);
}
});
};
var getLanguagesUsers = function(userId) {
$.ajax({
url: '/user_languages',
type: 'GET',
data: {userId: userId},
success: function(languages) {
for(var i=0; i<languages.length; i++) {
if(languages[i].level != 5) {
var id = languages[i].language_id
var langUrl = '/languages/'+id;
$.getJSON(langUrl, function(lang) {
var language = lang.language
var languageInfo = {
language: language
};
var languageTemplate = compiledLanguage(languageInfo);
$('#learningLanguages').append(languageTemplate);
});
} else {
var id = languages[i].language_id;
var langUrl = '/languages/'+id;
$.getJSON(langUrl, function(lang) {
var language = lang.language
var languageInfo = {
language: language
};
var languageTemplate = compiledLanguage(languageInfo);
$('#fluentLanguages').append(languageTemplate);
});
};
};
},
error: function(err) {
console.log(err);
}
});
};
};
$(document).ready(ready);
$(document).on('page:load', ready);
How can I make goToProfile() available to be called all the time?
Thanks!

You can do what you need by binding the click event handler at the same scope level as the function it needs to call...
var ready = function(){
$(document).on('click', '#goToProfile', function(e) {
e.preventDefault();
var value = $(this).attr('value')
var otherUserId = parseInt(value);
$('#profileSection').empty();
goToProfile(otherUserId);
});
// the rest of your ready function goes here
};
If you do it that way then the event handler has access to the goToProfile function as they're both within the ready function. Nothing outside that function can access the private methods inside it.

Related

Sending a post request via ajax is not working

I want to send an Ajax request when clicking a button but it seems my request is never executed.
Here is my HTML code :
<!DOCTYPE html>
<html lang="en">
<head>
<title>User Form</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src = "./actions.js"></script>
</head>
<body>
<div id="badFrm" class="container">
<h2><br>User Registration</h2>
<form id="Form" method="post">
<div class="form-group">
<label for="name">Name:</label>
<input type="name" class="form-control" id="name" placeholder="Enter Name" name="name">
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" id="email" placeholder="Enter Email" name="email">
</div>
<button id="submitBtn" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
and here is my javascript code :
i feel there is something wrong with my javascript code but i cant figure whats wrong ! i changed a lot of it based on the comments . what i want is when i click on the update button it changes to " submit again " and i want to replace "list items" ( name and eamil ) with input fields and put whatever written in them to be saved in the database instead . and eventually return to the first page which is the register form.
$(document).ready(function() {
var i ;
$("#submitBtn").click(function (e) {
e.preventDefault();
var name = $("#name").val();
var email = $("#email").val();
$.post("http://localhost/MiniProject/connect.php",
{
name: name,
email: email
}, function () {
var element = document.getElementById("badFrm");
element.remove();
showTbl();
});
function showTbl() {
$.post("http://localhost/MiniProject/Select.php",
{
name: name,
email: email
}, function (res) {
// console.log(res);
res = JSON.parse(res);
var html = '<ul id="List">';
for (i = 0; i < res.length; i++) {
var j = i +1 ;
html += '<li class = "name" >' + res[i].name + '</li><li class = "email">' + res[i].email + '</li>'+ '<div>' + '<button onclick="removeUser(this)" id="'+j+'" class="btn btn-primary">Remove</button>' + '<button onclick="updateUser(this)" id="'+j+'" class="btn btn-primary">Update</button>' + '</div>';
}
html += '</ul>';
document.body.innerHTML = html;
});
}
// function Update() {
// $.post("http://localhost/MiniProject/Update.php",
// {
// name: name,
// email: email
// }, function (res) {
// alert(res);
// });
// }
});
});
// $(document).ready(function() {
//
// $("#removeBtn").click(function (e) {
// e.preventDefault();
// var ID = document.getElementById("removeBtn");
// var element2 = document.getElementById("List");
// element2.remove();
// $.post("http://localhost/MiniProject/Remove.php",{
// id : ID
// }, function (res) {
// document.write(res);
// });
// });
//
// });
function removeUser(element){
var ID = element.id;
var element2 = document.getElementById();
element2.remove();
$.post("http://localhost/MiniProject/Remove.php",{
id : ID
}, function (res) {
console.log(res);
document.write(res);
});
//alert(element.id);
}
function updateUser(element){ // i need help in this part
var ID2 = element.id;
listItem = document.querySelector("li.name");
newNameInput = document.createElement('INPUT');
newNameInput.innerHTML = '<input type="name" class="form-control" id="name" placeholder="Enter New Name" name="name">';
listItem.parentNode.replaceChild(newNameInput, listItem);
listItem = document.querySelector("li.email");
newEmailInput = document.createElement('INPUT');
newEmailInput.innerHTML = '<input type="email" class="form-control" id="email" placeholder="Enter New Email" name="email">';
listItem.parentNode.replaceChild(newEmailInput, listItem);
// var Data = document.getElementbyId("lstitm");
// Data.remove();
// var Input = document.createElement("INPUT");
// Input.setAttribute('type','text');
$.post("http://localhost/MiniProject/Update.php",{
id : ID2,
}, function (res) {
console.log(res);
// document.write(res);
});
}
The issue is in the javascript code.
I can see that you are creating the button #removeBtn on the callback of the ajax request sent when the user clicks on the submit button, in showTbl().
Then you call removeUser() that is creating the handler for the newly created #removeBtn button.
The issue you are experiencing is that the ajax callback is asynchronous. So, you are attaching the handler on the #removeBtn before the button is created.
Here is what you should do :
$("#submitBtn").click(function (e) {
e.preventDefault();
var name = $("#name").val();
var email = $("#email").val();
$.post("http://localhost/MiniProject/connect.php",
{
name: name,
email: email
}, function () {
var element = document.getElementById("badFrm");
element.remove();
showTbl();
//removeUser(); <- remove that line
// Update();
}
);
function showTbl() {
$.post("http://localhost/MiniProject/Select.php",
{
name: name,
email: email
}, function (res) {
res = JSON.parse(res);
var html = '<ul id="List">';
for (var i = 0; i < res.length; i++) {
html += '<li>' + res[i].name + '</li><li>' + res[i].email + '</li>'+ '<div>' + '<button id="removeBtn" class="btn btn-primary">Remove</button>' + '<button id="updateBtn" class="btn btn-primary">Update</button>' + '</div>';
}
html += '</ul>';
document.body.innerHTML = html;
removeUser(); // <- call it there instead
}
);
}
I also recommend you yo rename your function removeUser() as it is not removing anything, it is just attaching the handler to the button.

AngularJS Why is my Controller not responsive?

This is a difficult question to ask, I will do my best to be brief:
I have a simple controller that I want to use to get information from an API and populate a selection list from trello.
Here is my controller:
function TrelloController($scope, $location, $routeParams, $timeout, dialogs, common){
var vm = this;
var controllerId = 'TrelloController';
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var logError = getLogFn(controllerId, 'error');
var scope = $scope;
var TRELLO = require("trello");
var key = '<my key>';
var token = '<my token>';
var trello = new TRELLO(key, token);
vm.title = "Trello Controller";
vm.addCard = addCard;
vm.getBoards = getBoards;
vm.toggle = toggle;
vm.getLists = getLists;
vm.getListsFromDictionary = getListsFromDictionary;
vm.isTrelloActive = false;
activate();
function activate(){
common.activateController([], controllerId)
.then(function () {
log('Activated Trello Controller');
initialise();
});
}
function initialise() {
vm.isTrelloActive = false;
getBoards();
getLists();
}
function toggle() {
vm.isTrelloActive = !vm.isTrelloActive;
log("TOGGLE CLICKED");
}
function addCard(cardName, cardDescription, listId) {
trello.addCard(cardName, cardDescription, listId, function (error, cardAdded) {
if (error) {
log("Could Not Add Card: ", error);
} else {
log("Card added: ", cardAdded.name);
}
});
}
function getBoards() {
trello.getBoards("me", function (error, boards) {
if (error) {
log("Could Not Get Boards: ", error);
} else {
log("found " + boards.length + " boards");
console.log(boards);
}
scope.boards = boards;
});
}
function getLists(){
for (var i=0; i<scope.boards.length; i++){
getListsWithBoardId(scope.boards[i].id, i);
}
}
function getListsWithBoardId(boardId, index){
trello.getListsOnBoard(boardId, function(error, lists){
if (error) {
log("Could Not Get Boards: ", error);
} else {
log("found " + lists.length + " lists on board:"+boardId);
console.log(lists);
}
scope.boards[index].lists = lists;
});
}
function getListsFromDictionary(boardId){
for (var i=0; i<scope.boards.length; i++) {
if(scope.boards[i].id == boardId){
return scope.boards[i].lists;
}
}
}
}module.exports = TrelloController;
This controller is intended to serve the purpose of governing my dialogue, simplified, this is that dialogue:
<div data-ng-controller="TrelloController as vm">
<div class="modal-header">
<img class="trelloLogo" name="trelloLogo" src="public/content/images/trello-mark-blue.png" alt="Add To Trello" ng-click="vm.toggle}">
<h3>{{parameter.heading}}</h3>
</div>
<div class="modal-body">
<form name="form">
<div ng-if="vm.isTrelloActive" class="form-group">
<label>Board</label>
<select name="typeInput" class="form-control" ng-required="true" ng-model="form.boardInput">
<option selected>Choose Board</option>
<option ng-repeat="board in scope.boards" value="{{board.id}}">{{board.name}}</option>
</select>
</div>
</form>
</div>
<!-- This section contains parts in the vm.addCard(...) that aren't included in this shortened version of The HTML template, I provided it with the additional fields for context of the API call at the end -->
<div ng-if="vm.isTrelloActive" class="modal-footer">
<button class="btn btn-primary" ng-disabled="!form.$dirty || !form.$valid" ng-click="vm.addCard(form.titleInput, form.descriptionInput, form.listInput)">Add To Board</button>
<button class="btn btn-default" ng-click="vm.isTrelloActive=false">Cancel</button>
</div>
</div>
When I am in the dialogue, Pressing the logo button appears to do nothing even though when it was previously set to: ng-click="vm.isTrelloActive = !vm.isTrelloActive" it would toggle the entire page. The activate method produces no logs and does not appear to run when pressed.
Why is this happening?

Issues with using AJAX and JQuery to multiselect and capture information from JSON file

I have a live search function that parses information from a JSON file using AJAX and jQuery, and then is clickable. What I'm struggling to figure out is how to have the value (in this case, "happy" or "fat") populate a multiselect, and then once that's accomplished, capture the rest of the data in that JSON array to be utilized later.
$(document).ready(function(){
$.ajaxSetup({ cache: false });
$('#search').keyup(function(){
$('#result').html('');
$('#state').val('');
var searchField = $('#search').val();
var expression = new RegExp(searchField, "i");
$.getJSON('coretype.json', function(data) {
$.each(data, function(key, value){
if (value.identifier.search(expression) != -1)
{
$('#result').append('<li class="list-group-item link-class"> '+value.identifier+'</li>');
}
});
});
});
$('#result').on('click', 'li', function() {
var click_text = $(this).text().split('|');
$('#search').val($.trim(click_text[0]));
$("#result").html('');
});
});
I have gotten all the way to having the value be clickable, and have been unsuccessful figuring out the rest from there.
Here's the JSON file:
[
{
"identifier":"Happy",
"progressbar1": 3,
"progressbar2": 2,
"progressbar3": -2
},
{
"identifier":"Fat",
"progressbar1": -3,
"progressbar2": -2,
"progressbar3": 2
}
]
Ideally I'd like javascript to be able to capture the "progressbarX" values when someone types in the identifier, although I figure there's a much easier way to accomplish this...
<!-- Search -->
<br /><br />
<div class="container" style="width:900px;">
<h2 align="center">EnneaTest</h2>
<br /><br />
<div align="center">
<input type="text" name="search" id="search" placeholder="trait type" class="form-control" />
</div>
<ul class="list-group" id="result"></ul>
<br />
</div>
</div>
</div>
Here's the Plunker file
I created a kind of autocomplete drop down for search from json. And once one of the options from that dropdown is selected, I add that to the result list. At that time the whole object is pushed into searchObjects object. When the item from the list is clicked, that text is used to search the object associated with it. Hope this helps..
<!-- Search -->
<br /><br />
<div class="container" style="width:900px;">
<h2 align="center">EnneaTest</h2>
<br /><br />
<div align="center">
<input type="text" name="search" id="search" placeholder="trait type" class="form-control" />
</div>
<div id="searchResult"></div>
<div>
<ul class="list" id="result" style="color: red;"></ul>
</div>
<br />
</div>
<script>
$(document).ready(function(){
$.ajaxSetup({ cache: false });
$('#search').keyup(function(){
var searchField = $('#search').val();
var regex = new RegExp(searchField, "i");
var output = '<div class="row">';
$.getJSON('coretype.json', function(data) {
$.each(data, function(key, val){
if (val.identifier.search(regex) !== -1) {
console.log(val);
var thisVal = JSON.stringify(val);
output += "<h5 onclick='addToList("+thisVal+")'>" + val.identifier + "</h5>";
}
});
output += '</div>';
$('#searchResult').html(output);
});
});
$('#result').on('click', 'li', function() {
var click_text = $(this).text();
console.log(click_text);
var thisObj = [];
thisObj = findObject(click_text);
console.log(thisObj);
});
});
var searchObjs = [];
function addToList(obj) {
//console.log(obj);
$('#result').append('<li class="list-group-item link-class">'+obj.identifier+'</li>');
$('#searchResult').html('');
var item = {};
item ["identifier"] = obj.identifier;
item ["progressbar1"] = obj.progressbar1;
item ["progressbar2"] = obj.progressbar2;
item ["progressbar3"] = obj.progressbar3;
searchObjs.push(item);
console.log(searchObjs);
}
function findObject(identifier) {
var found = 0;
for (var i = 0, len = searchObjs.length; i < len; i++) {
if (searchObjs[i].identifier === identifier) {
return searchObjs[i]; // Return as soon as the object is found
found = 1;
}
}
if(found === 0) {
return null; // The object was not found
}
} ;
</script>

Dropdown list not showing the saved value after page reload

I have a dropdownlist for Page location with 2 values. When I select a value from the list and click on Save, the value is saved in the database. But when I reload the page, the value which is first in the selection criteria is shown and when I edit other values and click on Save, the value for Page location is also changed. The issue is, I want to see the value which I changed for the Page location when the page is refreshed. Below is my code. Function 'edit' is called when I click on edit of the Page location.
HTML Code:
<script id="editTemplate" type="text/x-kendo-template">
<div class = "row">
<div class="col-sm-8">
<label style="display:none;">Id:</label>
<input type="hidden" id='ID' class="k-textbox" value="#=node.id #" />
</div>
<div class="col-sm-8">
<label >PageLocation:</label>
<select id = "pl">
<option value="local">local</option>
<option value="New Window" >New Window</option>
</select>
</div>
<div class="col-sm-4">
<button id="save" class="k-button k-primary">Save</button>
</div>
</div>
JavaScript:
function edit(itemid){
var editTemplate = kendo.template($("#editTemplate").html());
var treeview = $("#treeview").data("kendoTreeView");
var selectedNode = treeview.select();
var node = treeview.dataItem(selectedNode);
$("<div/>")
.html(editTemplate({ node: node}))
.appendTo("body")
.kendoWindow({
modal: true,
deactivate: function () {
this.destroy();
}
})
.on("click", ".k-primary", function (e) {
var dialog = $(e.currentTarget).closest("[data-role=window]").getKendoWindow();
var textbox = dialog.element.find(".k-textbox");
var Id = $('#ID').val();
var PageLocation = $('#pl').val();
node.text = undefined;
node.set("Pagelocation", PageLocation);
node.set("id", Id);
dialog.close();
var treenode = treeview.dataSource.get(itemid);
treenode.set("Pagelocation", PageLocation);
treenode.set("id", Id);
treenode.PAGE_LOCATION = PageLocation;
treenode.ID = Id;
$.ajax({
url: "/Services/TreeServices.asmx/UpdateTree",
contentType: "application/json; charset=utf-8",
type: "POST",
datatype: "json",
//data: JSON.stringify({ "erpLinksJson": treenode, NodeId: nid, RoleId: rid })
data: JSON.stringify({ "LinksJson": treenode})
});
console.log(JSON.stringify(treenode));
})
}
Service:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public String UpdateTree(LinksJSON LinksJson)
{
using (var context = new Data.WebEntities())
{
context.Configuration.ProxyCreationEnabled = false;
var updNode = context.Links.Where(c => c.ID == LinksJson.ID).FirstOrDefault();
if (updNode != null)
{
updNode.ID = erpLinksJson.ID;
updNode.PAGE_LOCATION = erpLinksJson.PAGE_LOCATION;
context.SaveChanges();
}
JavaScriptSerializer JSON = new JavaScriptSerializer();
return JSON.Serialize(updNode);
}
}

Binding a div row to a single item in an observableArray

I'm developing a 3 part upload form, where users can upload 3 sets of files
So far, I've got the following viewModel
var FileGroupViewModel = function (id) {
var self = this;
self.id = ko.observable(id);
self.files = ko.observableArray();
self.removeFile = function (item) {
self.files.remove(item);
}
self.fileUpload = function (data, e) {
var file = e.target.files[0];
self.files.push(file);
};
}
var ViewModel = function () {
var self = this;
self.fileGroups = ko.observableArray();
self.getFileGroupById = function (id) {
ko.utils.arrayFilter(self.fileGroups(), function (item) {
return item.id == id;
});
};
self.uploadFiles = function () {
alert('Uploading');
}
}
var viewModel = new ViewModel();
viewModel.fileGroups.push(new FileGroupViewModel(1));
viewModel.fileGroups.push(new FileGroupViewModel(2));
viewModel.fileGroups.push(new FileGroupViewModel(3));
ko.applyBindings(viewModel);
I have 3 'groups' of files a user can upload to.
(I will do the actual upload functionality later)
I'm struggling with how to bind my row to a specific item of the array?
Maybe I shouldn't use an observable array?
<div class="row files" id="files1" data-bind="???">
<h2>Files 1</h2>
<span class="btn btn-default btn-file">
Browse <input data-bind="event: {change: fileUpload}" type="file" />
</span>
<br />
<div class="fileList" data-bind="foreach: files"> <span data-bind="text: name"></span>
Remove
</div>
</div>
The idea is when a user selects files, they appear in a list under the button:
..with a link to remove the file from the upload queue.
I've set up a fiddle here - https://jsfiddle.net/alexjamesbrown/c9fvzjte/
There are few important modifications required to make your code work independently across files 0,1,2
KeyNote
event: { change: function(){fileUpload($data,$element.files[0])}}
here we are passing our selected file i.e filedata using $element in
change event not in usual click event . Filedata will have complete file information .
view:
<div class="row files" id="files1" data-bind="foreach:fileGroups">
<h2>Files 0</h2>
<span class="btn btn-default btn-file">
Browse <input data-bind="event: { change: function() { fileUpload($data,$element.files[0]) } }" type="file" />
</span>
<div class="fileList" data-bind="foreach: files"> <span data-bind="text: name"></span>
Remove
</div>
viewModel:
var SubFunction = function (data) {
var self = this;
self.name = data.name;
self.removeFile = function (item1) {
item1.files.remove(this); //current reference data & item1 has parent reference data
}
}
var FileGroupViewModel = function (id) {
var self = this;
self.id = ko.observable(id);
self.files = ko.observableArray([new SubFunction({
'name': 'Test'
})]);
self.fileUpload = function (item1, item2) {
self.files.push(new SubFunction(item2));
};
}
var ViewModel = function () {
var self = this;
self.fileGroups = ko.observableArray();
self.getFileGroupById = function (id) {
ko.utils.arrayFilter(self.fileGroups(), function (item) {
return item.id == id;
});
};
self.uploadFiles = function () {
alert('Uploading');
}
}
var viewModel = new ViewModel();
viewModel.fileGroups.push(new FileGroupViewModel(1));
ko.applyBindings(viewModel);
working sample up for grabs here
Working sample if you are planning to reuse your Html

Categories

Resources