I have a simple WebApi project to manipulate returned data in various format. I am trying to use KnockoutJs to consume the data at the front end but I am having a variable not defined error that I can't seem to understand why. Below is the simple code I am working on. Please feel free to point out the errors. Thanks
Controller formats
[httpGet]
public Object Data
{
return new {
Time: DateTime.now,
Text: "<b>Salut</b>",
Count: 1
};
}
JS front end
<script>
$(document).ready(function(){
$.ajax("/api/formats", {
success: function(data){
dataObject = ko.observable(data);
ko.applyBindings();
}
};
});
HTML
<tbody>
<tr>
<td>Time</td>
<td data-bind="text: dataObject().Time">
<td>Text</td>
<td data-bind="text: dataObject().Text">
</tr>
</tbody>
At first,your variable dataObject does not have Time and Text properties, so you should check it in your code as following:
var dataObject = ko.observable();
ko.applyBindings();
function doBinding() {
var data = {
Time: "XYZ",
Text: "<b>Salut</b>",
Count: 1
};
dataObject(data);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<div data-bind="text: dataObject() ? dataObject().Time : 'No Data'"></div>
<div data-bind="text: dataObject() ? dataObject().Text : 'No Data'"></div>
<div>
<button onclick="doBinding()">binding</button>
Related
I can get the elements sent from the backend.But the text can't display.I don't know why.
here is my html :
<!-- <form action=""> -->
<div class=" form-group">
<input v-model="wd" #keyup="keyup($event)" type="text" class="form-control" />
<!-- <ul class="list-group"> -->
<table>
<tr>
<th v-for="item in title"><div style="width:500px">{{item}}</div> </th>
</tr>
<tr v-for="item in arr" :key='item'>
<td>{{item.name}}</td>
<td>{{item.sort}}</td>
<td>{{item.company}}</td>
</tr>
</table>
</div>
here is My Vue:
el: "#app",
data: {
wd: '',
arr: [],
listIndex: -1,
title:['name','sort','company'],
},
methods: {
keyup(event) {
var url = "/search/search/"
axios.get(url, {
params: {
q: this.wd,
}
}).then(res => {
console.log(res);
this.arr = res.data.list;
})
}
}
})
;
I know the data was got.Because the number of the items in loop is exactly correct.When I console.log(res.data.list),I can view the data from the backend.But they can't display in my page.
I will be very appreciate it if you could help me.Thank you!
enter image description here
I think the problem is in your reference to this.arr in your callback. When the AJAX call returns, this is scoped to the response, not your Vue object.
You can overcome this by inserting a variable prior to the AJAX call, like this:
var vm = this;
axios.get(url, {
then, in the callback, instead of this.arr = res.data.list;, do this:
vm.arr = res.data.list;
The field data must be reactive
data() {
return{
wd: '',
arr: [],
listIndex: -1,
title:['name','sort','company'],
}
}
I have been doing lots of search on how to merge data from multiple collections and then send it to the Template using the Template Helper Function client side.
I referred to the solution given at : Merging collections in Meteor
Below is my Code:
Template HTML :
<template name="search_results">
<div class="col-md-12">
{{#if Template.subscriptionsReady}}
<table id="datatable" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>Type</th>
<th>Project Name</th>
<th>_id</th>
</tr>
</thead>
<tbody>
{{#each SearchData}}
<tr>
<td>{{search_type}}</td>
<td>{{name}}</td>
<th>{{_id}}</th>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
<div class="loading">{{>spinner}}</div>
{{/if}}
</div>
</template>
Template Helper Function:
Template.search_results.helpers({
SearchData: function() {
var project_name = Router.current().params.query.search;
var Projects = Projects.find({project_name: { $regex : project_name, $options:"i" } }).map( function ( Project ) { return { _id: Project._id, name: Project.project_name, search_type: 'Projects' }; }).fetch();
var CustomerAccounts = CustomerAccounts.find({account_name: { $regex : project_name, $options:"i" } }).map( function ( CustomerAccount ) { return { _id: CustomerAccount._id, name: CustomerAccount.account_name, search_type: 'Accounts' }; }).fetch();
var docs = Projects.concat(CustomerAccounts);
return docs;
}
});
I have tried the following code to send data from the single collection and its working fine:
return Projects.find({project_name: { $regex : project_name, $options:"i" } }).map( function ( Project ) { return { _id: Project._id, name: Project.project_name, search_type: 'Projects' }; });
But I want to merge two collections and send the data to Template, Where I am wrong and what is the good way to do it?
A cursor's map function returns an array, so calling fetch on that array would throw an error. Try removing the fetch calls and see if that helps.
I was doing one Typo Mistake Now the Issue is corrected:
Check My Rectified Code Below
var projects = Projects.find({project_name: { $regex : project_name, $options:"i" } }).map( function ( Project ) { return { _id: Project._id, name: Project.project_name, search_type: 'Projects' }; });
var customeraccounts = CustomerAccounts.find({account_name: { $regex : project_name, $options:"i" } }).map( function ( CustomerAccount ) { return { _id: CustomerAccount._id, name: CustomerAccount.account_name, search_type: 'Accounts' }; });
var docs = projects.concat(customeraccounts);
return docs;
I have created variable name same as Collection Name, then reailsed and then changed it to lowercase, instead of var Projects I changed it to var projects,
Second thing I have also removed fetch()
The issue is Solved now , As David Suggested me to remove fetch() , I am marking his Answer as Correct although my code has another issue of variable name.
I'm trying to make a table that shows products with angular, but the items are not showing up in HTML. If anyone notice anything wrong with the code, please help me.
This is my app.js:
(function (){
var app = angular.module('confirmados', []);
app.controler('ListaController', function (){
this.product = gem;
});
var gem = {
id: 1,
name: 'cool',
email: 'cool#'
};
And this is my HTML:
<tr ng-controller="ListaController as lista" class="reservas">
<td> {{lista.product.id}} </td>
<td> {{lista.product.name}} </td>
<td> {{lista.product.email}} </td>
</tr>
You spelled controller wrong:
app.controler
Should be
app.controller
I am new to knocokout.js so i have a table in which data is bind using ajax call.When user click on edit button row information is fill to a form which is on same page below the table.
after ajax call which update the data into database successfully , i am not able to show the changed value of particular object which is changed into table. If i refresh then its show new value .
Here is my html and js code .
<div id="body">
<h2>
Knockout CRUD Operations with ASP.Net Form App</h2>
<h3>
List of Products</h3>
<table id="products1">
<thead>
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Category
</th>
<th>
Price
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody data-bind="foreach: Products">
<tr>
<td data-bind="text: Id">
</td>
<td data-bind="text: Name">
</td>
<td data-bind="text: Category">
</td>
<td data-bind="text: formatCurrency(Price)">
</td>
<td>
<button data-bind="click: $root.edit">
Edit</button>
<button data-bind="click: $root.delete">
Delete</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
</td>
<td>
</td>
<td>
Total :
</td>
<td data-bind="text: formatCurrency($root.Total())">
</td>
<td>
</td>
</tr>
</tfoot>
</table>
<br />
<div style="border-top: solid 2px #282828; width: 430px; height: 10px">
</div>
<div data-bind="if: Product">
<div>
<h2>
Update Product</h2>
</div>
<div>
<label for="productId" data-bind="visible: false">
ID</label>
<label data-bind="text: Product().Id, visible: false">
</label>
</div>
<div>
<label for="name">
Name</label>
<input data-bind="value: Product().Name" type="text" title="Name" />
</div>
<div>
<label for="category">
Category</label>
<input data-bind="value: Product().Category" type="text" title="Category" />
</div>
<div>
<label for="price">
Price</label>
<input data-bind="value: Product().Price" type="text" title="Price" />
</div>
<br />
<div>
<button data-bind="click: $root.update">
Update</button>
<button data-bind="click: $root.cancel">
Cancel</button>
</div>
</div>
</div>
Code
function formatCurrency(value) {
return "$" + parseFloat(value).toFixed(2);
}
function ProductViewModel() {
//Make the self as 'this' reference
var self = this;
//Declare observable which will be bind with UI
self.Id = ko.observable("");
self.Name = ko.observable("");
self.Price = ko.observable("");
self.Category = ko.observable("");
var Product = {
Id: self.Id,
Name: self.Name,
Price: self.Price,
Category: self.Category
};
self.Product = ko.observable();
self.Products = ko.observableArray(); // Contains the list of products
// Initialize the view-model
$.ajax({
url: 'SProduct.aspx/GetAllProducts',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
// debugger;
$.each(data.d, function (index, prd) {
self.Products.push(prd);
})
//Put the response in ObservableArray
}
});
// Calculate Total of Price After Initialization
self.Total = ko.computed(function () {
var sum = 0;
var arr = self.Products();
for (var i = 0; i < arr.length; i++) {
sum += arr[i].Price;
}
return sum;
});
// Edit product details
self.edit = function (Product) {
self.Product(Product);
}
// Update product details
self.update = function () {
var Product = self.Product();
$.ajax({
url: 'SProduct.aspx/Update',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: "{Product:" + ko.toJSON(Product) + "}",
success: function (data) {
console.log(data.d);
self.Product(null);
alert("Record Updated Successfully");
},
error: function (data) {
console.log(data);
}
})
}
// Cancel product details
self.cancel = function () {
self.Product(null);
}
}
$(document).ready(function () {
var viewModel = new ProductViewModel();
ko.applyBindings(viewModel);
});
and my webmethod which called by ajax request is as follow :
// to update product
[WebMethod]
public static testModel.Product Update(testModel.Product Product)
{
testEntities db = new testEntities();
var obj = db.Products.First(o => o.Id == Product.Id);
obj.Name = Product.Name;
obj.Price = Product.Price;
obj.Category = Product.Category;
db.SaveChanges();
return obj;
}
and JSON response of ajax call as follow
{"d":{"__type":"testModel.Product","Id":31,"Name":"12","Category":"12","Price":1350,"EntityState":2,"EntityKey":
{"EntitySetName":"Products","EntityContainerName":"testEntities","EntityKeyValues":
[{"Key":"Id","Value":31}],"IsTemporary":false}}}
Here's what's happening. Here: self.Products.push(prd) prd is just a plain javascript object with plain property values, nothing is observable. You're pushing the raw object onto the Products observableArray, which updates the DOM because Products was changed and KO is watching it. When you click 'edit', you set self.Product to that plain object and the KO updates the DOM with this object and its values because Product was changed and KO is watching it. So now your Product form below displays, you see the information, and it looks like you can edit the properties but KO won't update those property changes because KO isn't watching them. They're not observable. Change:
$.each(data.d, function (index, prd) {
//self.Products.push(prd);
self.Products.push({
Id: ko.observable(prd.Id),
Name: ko.observable(prd.Name),
Price: ko.observable(prd.Price),
Category: ko.observable(prd.Category)
});
});
General helpful tips
<div data-bind="if: Product">
This only evaluates once when you bind the viewModel to the DOM with ko.applyBindings. Since self.Product has an initial value of null KO removes this altogether.*Note: I was thinking of #if for some reason.
This works like the visible binding except when the value is false, the element and its children are removed from the DOM. So there is more DOM manipulation going on than necessary. You probably just want to hide this <div>
I would recommend changing this to:
<div data-bind="visible: Product">
Instead of this:
<input type="text" data-bind="text: Product().Name" />
<input type="text" data-bind="text: Product().Category" />
<input type="text" data-bind="text: Product().Price" />
Try this instead:
<div data-bind="with: Product">
<input type="text" data-bind="text: Name" />
<input type="text" data-bind="text: Category" />
<input type="text" data-bind="text: Price" />
</div>
Consider renaming self.Product to self.SelectedProduct to make it more clear what it is for.
I'm not sure what this is doing in the ViewModel:
//Declare observable which will be bind with UI
self.Id = ko.observable("");
self.Name = ko.observable("");
self.Price = ko.observable("");
self.Category = ko.observable("");
var Product = {
Id: self.Id,
Name: self.Name,
Price: self.Price,
Category: self.Category
};
You don't use them in the DOM. You were kind of on the right path with this though. Instead, before the ProductViewModel, create this:
function ProductModel(data) {
var self = this;
data = data || {};
self.Id = ko.observable(data.Id);
self.Name = ko.observable(data.Name);
self.Price = ko.observable(data.Price);
self.Category = ko.observable(data.Category);
}
Now instead of:
$.each(data.d, function (index, prd) {
self.Products.push({
Id: ko.observable(prd.Id),
Name: ko.observable(prd.Name),
Price: ko.observable(prd.Price),
Category: ko.observable(prd.Category)
});
});
We can just do this:
$.each(data.d, function (index, prd) {
self.Products.push(new ProductModel(prd));
});
Hopefully that will get you headed in the right direction.
There is something to change:
Replace
$.each(data.d, function (index, prd) {
self.Products.push(prd);
})
With:
$.each(data.d, function (index, prd) {
self.Products.push({
Id: ko.observable(prd.Id),
Name: ko.observable(prd.Name),
Price: ko.observable(prd.Price),
Category: ko.observable(prd.Category)
});
})
Use ko.observable to make your properties notify the view of its changes so that the view can update accordingly. This should work but not perfect because this is 2 way binding, so whenever you update the values in your div, the view model object is updated immediately and even if your ajax fails to update the data in backend, making the data out of synch between client side and server side.
For better solution. You need to have a look at protectedObservable
$.each(data.d, function (index, prd) {
self.Products.push({
Id: ko.protectedObservable(prd.Id),
Name: ko.protectedObservable(prd.Name),
Price: ko.protectedObservable(prd.Price),
Category: ko.protectedObservable(prd.Category)
});
})
Inside your self.update ajax success function, trigger a change:
success: function (data) {
var product =self.Product();
product.Id.commit();
product.Name.commit();
product.Price.commit();
product.Category.commit();
self.Product(null);
alert("Record Updated Successfully");
}
And revert if there is error:
error: function (data) {
product.Id.reset();
product.Name.reset();
product.Price.reset();
product.Category.reset();
}
Update:
Remember to change all the places with Product.Property to Product.Property() to get its property value . For example: arr[i].Price should be changed to arr[i].Price()
Add self.Products.push(data.d); to the update() functions success handler.
success: function (data) {
console.log(data.d);
self.Product(null);
self.Products.push(data.d);
alert("Record Updated Successfully");
},
You need to update the array so that it reflects in bound html.
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.