I'm trying to build a simple search feature, but I can't figure out why my code is not working.
This is the action that I have built to search:
search: function(req, res) {
var criteria = req.param('criteria');
var value = req.param('value');
Human.find().where({ criteria: value }).done(function(err, humans) {
if(err) {
return console.log('Error:' + err);
}else{
res.view({
title: 'Search',
humans: humans
});
}
});
}
I have a button on my main page with the ID of search. I want to make it so that whenever someone clicks my search button, it queries the database and returns the results at localhost:1337/model/search. So far, I've tried sending an ajax request to that controller action with the two variables (criteria, value) but it doesn't work.
This is the ajax call that I am submitting:
$('#search').on('click', function() {
var criteria = $('#filter').val();
var value = $('#value').val();
$.ajax({
type: 'POST',
url: 'http://localhost:1337/human/search',
cache: false,
data: {
criteria: criteria,
value: value
},
success: function(data) {
console.log('SUCCESS!');
window.location.href = 'http://localhost:1337/human/search';
},
error: function(err) {
console.log('ERROR!');
}
});
});
And this is the corresponding view:
<table>
<thead>
<tr>
<th>ID</th>
<th width="150">First Name</th>
<th width="150">Last Name</th>
<th width="150">Contact</th>
<th width="150">E-Mail</th>
<th>View</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
<% _.each(humans, function(model) { %>
<tr>
<td> <%= model.id %> </td>
<td> <%= model.firstName %> </td>
<td> <%= model.lastName %> </td>
<td> <%= model.contact %> </td>
<td> <%= model.email %> </td>
<td>VIEW</td>
<td>EDIT</td>
</tr>
<% }) %>
</tbody>
</table>
Promlem #1: When you search the model like this: Human.find().where({ criteria: value }), you actually search by field named "criteria", instead of searching by field, which name is held in criteria variable.
Try to create search object like this:
var searchObj = {};
searchObj[criteria] = value;
// and then search like you did before
Human.find().where(searchObj).done(function(err, humans) {
if(err) {
console.log('Error:' + err);
// you should return some response here:
res.send(err, 500);
}else{
res.view({
title: 'Search',
humans: humans
});
}
});
Problem #2: why you do ajax request and then do redirect to the same url?
First, you make POST request, although GET request is more suitable for search pupposes. POST is usually used when you create resources.
Second, in ajax success handler, after you receive the view with found humans models, you just redirect browser to http://localhost:1337/human/search url without any parameters passed, so your controller will try to search by empty value and criteria Human.find().where({ "": "" }). So you'll not see expected result.
It's not clear whether you want to get data via ajax, or just to show it in new HTML page?
EDIT: If you don't want to use ajax, let the HTML form do the work for you:
<form action="human/search">
<input name="criteria" value="foo">
<input name="value" value="bar">
<button type="submit" id="search">Search</button>
</form>
The search button click will submit the form and pass all form data in the GET request's query string: http://localhost:1337/human/search?criteria=foo&value=bar
Of course, you can build query string manually with javascript, without using form, and redirect browser to that url. Result will be the same.
Related
I am currently working in Asp.net MVC in Visual Studio 2017. I am new to working with Asp.net MVC and can not get the values i am needing.
I have a table of questions that are all displayed in a table on my view page. Each one of the questions have a Boolean value that shows as check boxes when they are being displayed. I am currently trying to write a script to get the checkbox value of true or false and where that checkbox is checked I am trying to get the Id of the question in the current row.
This is my view where I am displaying the questions.
#model IEnumerable<CapstoneApplication.Models.Question>
#{
ViewBag.Title = "Index";
}
<h2>Questions: Admin View</h2>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/SaveCheckBox.js"></script>
<p>
#Html.ActionLink("Create Question", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.IsPartOfGame)
</th>
<th>
#Html.DisplayNameFor(model => model.Category.CategoryName)
</th>
<th>
#Html.DisplayNameFor(model => model.QuestionDescription)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.CheckBoxFor(modelItem=>item.IsPartOfGame, new {onclick = "SaveCheckBox(this)" })
</td>
<td>
#Html.DisplayFor(modelItem => item.Category.CategoryName)
</td>
<td>
#Html.DisplayFor(modelItem => item.QuestionDescription)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new {id = item.Id}) |
#Html.ActionLink("Delete", "Delete", new {id = item.Id})
</td>
</tr>
}
</table>
This is the script I am currently using.
function SaveCheckBox(checkboxInput) {
$.ajax({
type: 'GET',
url: 'Index',
data: {idValue: checkboxInput.closest("tr").getAttribute("id"),
newValue: checkboxInput.checked },
dataType: 'json'
});
}
Finally this is the method the script calls to give the values to.
public ActionResult Index(bool? newValue, int? idValue)
{
//save to database here
var questions = db.Questions.Include(q => q.Category);
return View(questions.ToList());
}
The problem I am having is that newValue always returns the correct value of being either true or false but idValue is always null it never grabs the id of the question in the row of the checkbox that was checked.
I would place the id as the data attribute in your CheckboxFor
#Html.CheckBoxFor(
modelItem=>item.IsPartOfGame
, new {onclick = "SaveCheckBox(this)", data_itemid = item.Id}
})
Then I would use this for your javascript
function SaveCheckBox(checkboxInput) {
$.ajax({
type: 'GET',
url: 'Index',
data: {
idValue: checkboxInput.dataset.itemid
, newValue: checkboxInput.checked
},
dataType: 'json'
});
}
I have a table that I am using jQuery Datatables with.
Picture:
Scenario:
As you can see in the picture, there is a Delete link. When that link is clicked, a modal pop-up will show asking the user if they really want to delete that item. If yes, delete.. if no.. cancel out of the modal.
What I want:
When a user decides to delete an item and confirms it.. I would like to change the status of that item to "Deleted", via ajax. I am able to change the value, but that value does not show in the table. I have researched this for a couple of days now, but nothing seems to work.
My Code
<table id="Item-Table" class="table table-bordered">
<thead>
<tr>
<th class="text-center">
#Html.DisplayNameFor(model => model.AssetTag)
</th>
<th class="text-center">
#Html.DisplayNameFor(model => model.codeMakeModel.MakeModel)
</th>
<th class="text-center">
#Html.DisplayNameFor(model => model.codeStatu.Status)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr class="text-center">
<td>
#Html.ActionLink(item.AssetTag, "Edit", new { id = item.Id })
</td>
<td>
#Html.DisplayFor(modelItem => item.codeMakeModel.MakeModel)
</td>
<td class="changeStatus">
#Html.DisplayFor(modelItem => item.codeStatu.Status)
</td>
<td>
Delete
</td>
</tr>
}
</tbody>
</table>
#section scripts{
<script>
var settings = {};
settings.baseUri = '#Request.ApplicationPath';
var infoGetUrl = "";
if (settings.baseUri === "/projectonservername") {
infoGetUrl = settings.baseUri + "/api/itemsapi/";
} else {
infoGetUrl = settings.baseUri + "api/itemsapi/";
}
$(document).ready(function () {
var itemsTable = $("#Item-Table").DataTable({
"aoColumnDefs": [
{ "bSortable": false, "aTargets": [3] },
{ "bSearchable": false, "aTargets": [3] }
]
});
$("#Item-Table").on("click",
".js-item-delete",
function() {
var link = $(this);
bootbox.confirm({
title: "Delete Item?",
message: "Are you sure you want to delete this item?",
buttons: {
cancel: {
label: '<i class="fa fa-times"></i> Cancel'
},
confirm: {
label: '<i class="fa fa-check"></i> Confirm'
}
},
callback: function(result) {
if (result) {
toastr.options = {
timeOut: 5000
}
$.ajax({
url: infoGetUrl + link.data("item-id"),
method: "DELETE",
success: function (result) {
//itemsTable.cell(itemsTable.row(this), 2).data("Deleted");
//itemsTable.draw();
//itemsTable.reload();
console.log(itemsTable.cell(itemsTable.row(this), $('.changeStatus')).data());
itemsTable.cell(itemsTable.row(this), $('.changeStatus')).data("Deleted").draw();
console.log(itemsTable.cell(itemsTable.row(this), $('.changeStatus')).data());
toastr.success("Item successfully deleted");
},
error: function(jqXHR, textStatus, errorThrown) {
var status = capitalizeFirstLetter(textStatus);
console.log(jqXHR);
toastr.error(status + " - " + errorThrown, "Sorry, something went wrong.");
}
});
}
}
});
});
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
})
</script>
}
What I am Receiving
In the above code, specifically these lines:
console.log(itemsTable.cell(itemsTable.row(this), $('.changeStatus')).data());
itemsTable.cell(itemsTable.row(this), $('.changeStatus')).data("Deleted").draw();
console.log(itemsTable.cell(itemsTable.row(this), $('.changeStatus')).data());
I am logging the value of the cell before I update that cell value, then changing the cell value, then logging the new/updated cell value.
Here is what I am receiving in the console:
But the table is not updating, or rather.. redrawing itself to show deleted.. the only way for it show deleted is to refresh the page which defeats the purpose of ajax..
How do I get the table to update the cell value without a page refresh?
Any help is appreciated.
I was able to answer this myself with some help of DavidDomain in the comments.
He suggested that I could possibly be selecting an incorrect row. So that gave me the idea to get the row at the start of this by adding:
$("#Item-Table").on("click",
".js-item-delete",
function() {
var link = $(this);
var row = $(this).parents("tr"); // get row element
Then set the cell data using that variable like so:
itemsTable.cell(itemsTable.row(row), $('.changeStatus')).data("Deleted").draw();
This worked and successfully drew the table with the updated value.
I have an interesting problem while reloading partial views with ajax. I have a following setup in the master View:
<div>
<div id="items">
#Html.Partial("SubView", Model.Items);
</div>
<div>
SubView is generally a list of items in a table as follows:
<table class="table table-striped">
<tr>
<th>Date</th>
<th>Time</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
#Html.HiddenFor(modelItem => item.Id)
<td>#Html.DisplayFor(modelItem => item.Date)</td>
<td>#Html.DisplayFor(modelItem => item.Time)</td>
<td>
#Html.ActionLink("Delete", "Delete", new { itemId= item.Id, page = Model.PageNumber }, new { #class = "deleteItem" })
</td>
</tr>
}
DeleteItem Action in the controller does basically the following:
[HttpDelete]
public ActionResult DeleteItem(int itemId, int page)
{
this.dbService.DeletItem(expenseId);
return PartialView("SubView", this.GetPagedList(page));
}
In the script that I refer in the master View I have the following code:
$(document).ready(function () {
// delete expense
$(".deleteItem").click(function () {
$.ajax({
url: this.href,
type: 'delete',
success: function (result) {
$("#items").html(result);
}
});
return false;
});
This works fine the first time, but the second time it only loads the partial View -> since the JavaScript code is not being executed...
I am relatively new to that stuff and I am a bit confused what's going on here.
All 3rd party scripts are rendered in the Layout.cshtml
You can't attach a .click() event to a dynamically generated item. You have to structure it this way:
$(document).on('click', '.deleteItem', function() {
// Deletey stuff here.
});
For partial views to render, you have to make the return type PartialViewResult rather than ActionResult. Because ActionResult causes a redirection.
Try editing your DeleteItem function like this.
[HttpDelete]
public PartialViewResult DeleteItem(int itemId, int page)
{
this.dbService.DeletItem(expenseId);
return PartialView("SubView", this.GetPagedList(page));
}
Hi I'm using angularJs in my client side. I have a view where a user can add and remove item like this:
app.controller('demoController', function($scope) {
// initial items
$scope.items = [
'item one',
'item two',
'item three'
];
// add an item
$scope.add = function() {
$scope.items.push($scope.input);
};
// remove an item
$scope.remove = function(index) {
$scope.items.splice(index, 1);
};`enter code here`
});
When the user finish, I want he clicks on a button. And after I will send all the items added and removed to a server to update database. I can't do this on each click because I need all the information to fill an email in my server part. I know how to remove and add item but I don't how to found removed items and add items and send them to the server. Please any one know how I can do this?
Thanks a lot.
You can do it with only using 1 array. You just have to create a new property and set it to true - if removed -, or false otherwise.
Then in your back-end you can get all the removed items accessing this property.
See the example below:
(function() {
'use strict';
angular
.module('app', [])
.controller('demoController', demoController);
demoController.$inject = ['$scope'];
function demoController($scope) {
// initial items
$scope.items = [
{
"name":"item one",
"removed":false
},
{
"name":"item two",
"removed":false
},
{
"name":"item three",
"removed":false
}
];
// add an item
$scope.add = function() {
$scope.items.push({
"name": $scope.input,
"removed": false
});
};
// remove an item
$scope.remove = function(index) {
$scope.items[index].removed = !$scope.items[index].removed;
};
}
})();
<!DOCTYPE HTML>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" />
</head>
<body ng-controller="demoController">
<table class="table table-hover">
<thead>
<tr>
<td>Name</td>
<td>Removed?</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items track by $index">
<td ng-bind="item.name"></td>
<td ng-bind="item.removed"></td>
<td>
<button type="button" class="btn btn-danger btn-sm" ng-click="remove($index)">Remove item</button>
</td>
</tr>
</tbody>
</table>
<hr>
<input type="text" class="form-control" ng-model="input" placeholder="Type to add">
<button type="button" class="form-control btn btn-primary btn-sm" ng-click="add()">Add item</button>
</body>
</html>
Note: If you don't want to show the removed items, you can simply check in your tr:
<tr ng-repeat="item in items track by $index" ng-if="!item.removed">
Now if you want to send both added and removed you have to actually store the removed ones somewhere either in the object itself with a flag like #developer033 suggested or either in an other object.
For me it is better to store all added and removed elements in one object. Now you do not need to click a button and send the change on every add or remove. When you are done with adding and removing you can just simply send the whole object with AJAX request to the server where you can do your logic:
function demoController($scope, $http, $q) {
$scope.submitItems = function(){
sendItems($scope.items).then(function () {
alert("Successfully deleted PT");
}, function (error) {
alert(error);
});
};
// ....
var sendItems = function (items) {
var request = $http({
url: _SERVER + 'edit/sendItems', // for example
method: 'POST',
data : items
params: {
}
});
return request.then(function (data) {
return $q.when(data);
}, function (error) {
return $q.reject(error);
});
}
// ....
}
It is a good practise to have a service from where you call the server and where this method sendItems should be. But we try to keep at as simple as possible.
Now in your rest controller in Spring you have to specify #RequestBody param:
#RequestMapping(value = "/sendItems", method = RequestMethod.POST)
public String editProductParameters(#RequestBody ArrayList<Item> items) {
//your logic goes here
return "Success"
}
Where the Item.class should consist the fields: String name and boolean remove also should have a deffault constructor(deffault constructur is specified if there are none implementations of constructurs in the class or if there is a constructor with no arguments) also create getters and setter about the two fields. Thouse are the requirements that are needed to pass the whole array of objects($scope.items) from the client to the server with default mapping.
Good luck
It probably goes without saying that I'm quite new to angular as I'm trying to accomplish a relatively simple task, and I've come here for some help
I'm recreating our company's password vault using angular.
Here is what I am trying to accomplish.
The page loads with a list of accounts. Most the information is visible except for the password. I have a button that when clicked, hides the button, queries the database, logs who queried password, and displays the password to the user. The passwords are clear text because they aren't passwords for client accounts or anything sensitive, it exists for our employees to reference how/where to login to various websites we use for day to day business.
My HTML looks as follows:
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}</td>
<td>{{account.Username}}</td>
<td><button ng-click="showPassword(account.AccountId);" class="btn btn-primary">View</button><span></span></td>
<td>{{account.Comments}}</td>
</tr>
My scope controller looks as follows
$scope.showPassword = function (accountId) {
passwordVaultData.getAccountPassword(accountId)
.$promise
.then(function (r) {
//success
}, function (r) {
//fail
});
}
My showPassword() method works and returns the correct password, but I can't figure out how to hide the button and display the password.
Using the ng-show and ng-hide directives against the password on the account object should suffice for modifying the UI
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}</td>
<td>{{account.Username}}</td>
<td>
<button ng-hide="account.Password" ng-click="showPassword(account.AccountId);" class="btn btn-primary">View</button>
<span ng-show="account.Password">{{account.Password}}</span>
</td>
<td>{{account.Comments}}</td>
</tr>
As for the promise resolution, you want the getAccountPassword to return a promise, I will make an assumption about it's content below
function getAccountPassword(account) {
var deferred = $q.defer();
$http.get('api/vault/' + account.AccountId).then(function(r) {
deferred.resolve(r);
}, function(r) {
deferred.reject(r);
});
return deferred.promise;
}
$scope.showPassword = function (account) {
getAccountPassword(account.AccountId).then(function(password) {
account.Password = password;
}, function(password) {
account.Password = undefined; // some type of error handling here
});
}
Because the promise is executed in the context of an $http call, the digest cycle will run and the elements will be shown based on whether password is populated.
You can accomplish it by either ng-if or ng-show/hide:
Quick sample below:
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}</td>
<td>{{account.Username}}</td>
<td>
<button ng-if="!account.password" ng-click="showPassword(account);" class="btn btn-primary">View</button><span></span></td>
<span ng-if="account.password">{{password}}</span>
<td>{{account.Comments}}</td>
</tr>
$scope.showPassword = function (account) {
account.password = passwordVaultData.getAccountPassword(account.AccountId)
.$promise
.then(function (r) {
//success
}, function (r) {
//fail
});
}
Please see demo below
var app = angular.module('app', []);
angular.module('app').
controller('firstCtrl', function($scope) {
$scope.resp = {
PasswordVaultAccounts: [{
AccountId: 1,
URL: "bbc.co.uk",
Username: "Jack",
Comments: "White"
}, {
AccountId: 2,
URL: "bbc.co.uk",
Username: "Mike",
Comments: "Green"
}, {
AccountId: 3,
URL: "bbc.co.uk",
Username: "Tim",
Comments: "Red"
}
]
}
$scope.showPassword = function(account) {
//call you backend and on sucess add that :
// passwordVaultData.getAccountPassword(account.accountId)
// .$promise
// .then(function (r) {
account.showpass = true;
account.pass = account.Username + " password is *****"
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="firstCtrl">
<table>
<tr ng-repeat="account in resp.PasswordVaultAccounts">
<td>{{account.Name}}
</td>
<td>{{account.Username}}</td>
<td>
<button ng-click="showPassword(account);" class="btn btn-primary" ng-hide="account.showpass">View</button>
<span ng-show="account.showpass">{{account.pass}}</span>
</td>
<td>{{account.Comments}}</td>
</tr>
</table>
</div>
</body>