How can I aggregate when I'm mapping in KnockoutJS? - json

I'm working with KnockoutJS for a few days now and I have just faced with a simple problem that I cannot solve!
I have a model like this:
function Contact() {
var self = this;
self.name = ko.observable("");
self.phones = ko.observableArray([]);
self.faxes = ko.observableArray([]);
self.address = ko.observable("");
}
I have to call a service which will return repeatitive rows if each contact contains more that one phone or fax. e.g. :
{{"name":"Name1","phone":"1234","fax":"5654","address":"abc"},{{"name":"Name1","phone":"2323","fax":"8787","address":"abc"}}
I do a call like to server like this:
self.GetContacts = function () {
$.getJSON(self.queryUrl(),
function (data) {
if (data.results) {
self.contacts(ko.toJS(data.results));
}
});
There is no way to aggregate it on the serverside as it is an out-of-the-box tool and the only possible way is to aggregate it on the client side.
How can I aggregate them using my custom model, before binding them?

I would do something like that :
function aggragateContacts(data) {
var contacts = [];
ko.utils.arrayForEach(data, function (contactData) {
// looking an existing contact with the same name and address
var contact = ko.utils.arrayFirst(contacts, function (c) {
return c.name() == contactData.name && c.address() == contactData.address;
});
// otherwise create one
if (contact == null) {
contact = new Contact();
contact.name(contactData.name);
contact.address(contactData.address);
contacts.push(contact);
}
contact.phones.push(contactData.phone);
contact.fax.push(contactData.fax);
});
}
self.GetContacts = function () {
$.getJSON(self.queryUrl(),
function (data) {
if (data.results) {
self.contacts(aggragateContacts(ko.toJS(data.results)));
}
})
}

Related

Loading different modal based on knockout.js value

So I have a table of people's information and I used codes that look something like below (people's location in this case):
<!-- ko foreach: filteredApplications -->
<div class="col-md-6">
<span>Location:</span>
<span data-bind="text: application.candidateLocation"></span>
</div>
to display location information for all people.
And when I click the "Preview Application" link, it is supposed to give me a modal like the attached image and show the information of the corresponding person.
I tried to remove the foreach knockout for the modal, but then it gives me an error saying that it cannot find the application.candidateLocation variable.
How do I have to approach this issue?
Please help!
EDIT (viewmodel):
public partial class Application
{
public dynamic JsonForm => new
{
CandidateLocation,
Job = this.Job.JsonForm,
CandidateStatus = this.CurrentStatuse.CandidateStatus,
};
public string CandidateLocation
{
get
{
switch (ApplicationCandidateType)
{
case ApplicationCandidateType.Standard:
return InsideApplication.CandidateApplication.Candidate.Location;
case ApplicationCandidateType.Guest:
return null;
case ApplicationCandidateType.Manual:
return null;
default:
throw new Exception("Unhandled ApplicationCandidateType");
}
}
}
function ViewModel() {
var self = this;
self.invite = ko.observable(false);
self.changeStylesInvite = function () {
self.invite(true);
}
self.notifications = ko.observableArray(#Html.Json(Model.Notifications.Select(o => o.JsonForm)) || []);
self.applications = ko.observableArray(#Html.Json(Model.ApplicationCompatibilities.Select(o => o.JsonForm)) || []);
self.applicationInvitations = ko.observableArray(#Html.Json(Model.ApplicationInvitations.Select(o => o.JsonForm)) || []);
self.applicationsFilter = ko.observable("new");
self.showHiddenApplications = ko.observable(false);
self.newApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return !i.application.isShortlisted && !i.application.isContactInfoSent && (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});
self.shortlistedApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return i.application.isShortlisted && (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});
self.connectedApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return i.application.isContactInfoSent && (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});
self.allApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});

need signalR polling for asp.net mvc

Any kind soul can guide me how to use signalR on an existing mvc project to poll data in real time i'd be greatly appreciate.
example code:
[controller]
private ApplicationDbContext db = new ApplicationDbContext();
public PartialViewResult Chat(string people) // <---need to send real time data to partial
{
var model = new MessageVM()
{
sender = User.Identity.Name;,
messageList = db.messages.Where(x => x.receiver == people).ToList().Take(30)
};
return PartialView("_chat", model);
}
[view]
#Ajax.ActionLink(item.name, "Chat", new { people = item.name }, new AjaxOptions()
{ HttpMethod = "GET", UpdateTargetId = "divChat", InsertionMode = InsertionMode.Replace })
<div id="divChat"></div> // <---this area need real-time messages data from controller.
First create your signalr connection in js in client side. something like:
function signalrconnection() {
$.connection.hub.url = "http://localhost:54321/signalr";
chat = $.connection.myHub;
if (chat != undefined) {
$.connection.hub.start()
.done(function () {
chat.server.send("client", "Status\tasking for status");
chat = $.connection.myHub;
})
.fail(function () { NoLTConnectionAlert(); });
}
else {
///do something.
}
}
return chat;
}
Then add signalr call to your $(document).ready(function ()) in your js something like:
$(document).ready(function () {
chat = signalrconnection();
intervalstatus = setInterval(checkstatus, 1000);
// Create a function that the hub can call to broadcast messages.
chat.client.addMessage = function (name, message) {}
}
In your controller you should have a class for hub and method inside like:
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.Caller.addMessage("parameter", reply);
}
}
Then again you should handle Clients.Caller.addMessage in you js to update <div id="divChat"></div>

Setting data in viewModel knockoutjs from html5 websocket

I am trying to create knockout.js component that is getting data from HTML5 Websocket. Websocket code is in separate script e.g. util.js. I am able to connect and get data from socket, but dont know how correctly to set corresponding property in component`s ViewModel.
Websocket - util.js:
var options = {
server: '127.0.0.1',
port: '12345'
};
var socket, loadedFlag;
var timeout = 2000;
var clearTimer = -1;
var data = {};
function handleErrors(sError, sURL, iLine)
{
return true;
};
function getSocketState()
{
return (socket != null) ? socket.readyState : 0;
}
function onMessage(e)
{
data=$.parseJSON(e.data);
// ???? Is it possible to have here something like
// ???? viewModel.getDataWS1(data);
}
function onError()
{
clearInterval(clearTimer);
socket.onclose = function () {
loadedFlag = false;
};
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onClose()
{
loadedFlag = false;
clearInterval(clearTimer);
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onOpen()
{
clearInterval(clearTimer);
console.log("open" + getSocketState());
}
function connectWebSocket()
{
if ("WebSocket" in window)
{
if (getSocketState() === 1)
{
socket.onopen = onOpen;
clearInterval(clearTimer);
console.log(getSocketState());
}
else
{
try
{
host = "ws://" + options.server + ":" + options.port;
socket = new WebSocket(host);
socket.onopen = onOpen;
socket.onmessage = function (e) {
onMessage(e);
};
socket.onerror = onError;
socket.onclose = onClose;
}
catch (exeption)
{
console.log(exeption);
}
}
}
}
Component (productDisplay.js) - creating so that is can be used on multiple pages:
define([
'jquery',
'app/models/productDisplayModel',
'knockout',
'mapping',
'socket'
],
function ($, model, ko, mapping) {
ko.components.register('product', {
viewModel: {require: 'app/models/productModel'},
template: {require: 'text!app/views/product.html'}
});
});
Product ViewModel (productModel.js) - where I struggle to set viewModel property to data from websocket:
var viewModel = {};
define(['knockout', 'mapping', 'jquery'], function (ko, mapping, $) {
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.getDataWS1 = function () {
//Websocket has not connected and returned data yet, so data object is empty
// ???? Is there anyway I can add something like promise so that the value is set once socket is connected?
this.products(data);
};
// apply binding on page load
$(document).ready(function () {
connectToServer1();
viewModel = new MyViewModel();
ko.applyBindings(viewModel);
viewModel.getDataWS1();
});
});
Thank you for any ideas.
You can update an observable when you get a message in the following manner:
util.js
function onMessage(e) {
var productData = $.parseJSON(e.data);
viewModel.addNewProduct(productData);
}
productModel.js
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.addNewProduct(product) {
var newProduct = new Product(product.name, product.rating);
this.products.push(newProduct);
}
Basically the idea is that when you get a message (in onMessage function), you will parse the data and call a function in your viewmodel to add the message data to the viewmodel properties (observables, observableArrays, etc.)

Angularjs ajax request in Symfony2 and Doctrine json response with relationships

I am trying to work with Symfony2, Doctrine and Angujarjs. There is no problem with Symfony2 or Doctrine but I have issues using an ajax request with angularjs. Either it doesn't load anything and I did make a mistake while loading the json (json comes from Symfony and its working) or if it's working, but the json doesn't contain any of the relationship's data.
So, what's the correct way to
A: create a response for angularjs with relationship data (such as articles and categories)
B: load the requested json into an angularjs variable
Here is my controller.js
var app = angular.module("MyApp", []) .config(['$interpolateProvider', function ($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
}]);
app.filter('offset', function() {
return function(input, start) {
start = parseInt(start, 10);
return input.slice(start);
};
});
app.filter('htmlToPlaintext', function() {
return function(text) {
return String(text).replace(/<[^>]+>/gm, '');
};
});
app.controller("PaginationCtrl", function($scope, $http) {
$scope.articlesPerPage = 8;
$scope.currentPage = 0;
function htmlToPlaintext(text) {
return String(text).replace(/<[^>]+>/gm, '');
}
// this should load the json from '/admin/jsonallarticles' into the articles variable
$http.get('/admin/jsonallarticles').success(function(data) {
$scope.articles = data;
});
$scope.range = function() {
var rangeSize = 5;
var ret = [];
var start;
start = $scope.currentPage;
if ( start > $scope.pageCount()-rangeSize ) {
start = $scope.pageCount()-rangeSize+1;
}
for (var i=start; i<start+rangeSize; i++) {
ret.push(i);
}
return ret;
};
$scope.prevPage = function() {
if ($scope.currentPage > 0) {
$scope.currentPage--;
}
};
$scope.prevPageDisabled = function() {
return $scope.currentPage === 0 ? "disabled" : "";
};
$scope.pageCount = function() {
return Math.ceil($scope.articles.length/$scope.articlesPerPage)-1;
};
$scope.nextPage = function() {
if ($scope.currentPage < $scope.pageCount()) {
$scope.currentPage++;
}
};
$scope.nextPageDisabled = function() {
return $scope.currentPage === $scope.pageCount() ? "disabled" : "";
};
$scope.setPage = function(n) {
$scope.currentPage = n;
};
});
This is my symfony2 function
public function jsonallarticlesAction() {
$articles = $this->getDoctrine()
->getRepository('AcmeBlogBundle:Articles');
if ( !$articles ) {
throw $this->createNotFoundException(
'Keine Beiträge gefunden!');
}
$queryArticles = $articles->createQueryBuilder('a')
->where('a.status = :status')
->setParameter('status', 0)
->orderBy('a.createdDate', 'DESC')
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);;
$articles = array_values($queryArticles);
$response = new Response();
$response->setContent(json_encode($articles));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
EDITED CONTROLLER
I tried using the serializer which comes with Symfony
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$articles = $this->getDoctrine()
->getRepository('AcmeBlogBundle:Articles')
->findAll();
if ( !$articles ) {
throw $this->createNotFoundException(
'Keine Artikel gefunden!');
}
$serializer->serialize($articles, 'json');
return new Response(json_encode($json));
But I receive an error:
A circular reference has been detected (configured limit: 1).
For work with Angular.js you must write Rest API. For this you can use https://github.com/FriendsOfSymfony/FOSRestBundle
And for serialize your entities with needed data use http://jmsyst.com/bundles/JMSSerializerBundle
It compatible with FOSRestBundle.
As example of use those bundles you can look one our project https://github.com/stfalcon-studio/lost-and-found
I ran into the same issue and it was due to the fact that my Entity was related back to the same entity from my second entity on a different field. I just simply created this function in my Entity:
public function removeRelationsThatCauseCircularError()
{
$this->companyEvents = NULL;
}
And run the function before going through the serializer.

Define json specific field is declared or not

I have Asp.Net Mvc4 application. In one Action method I have conditional process that return different json result as follows:
if(true)
{
return Json(new { count = cartItm.ProductCount, total = cartItm.TotalAmount });
}
else
{
return Json(new
{
thumb = item.ThumbnailPhoto,
productName = item.Name,
itemCount = cartItem.ProductCount,
itemTotal = cartItem.TotalAmount,
productTotal = cart.TotalAmount,
productCount = cart.CartItems.Sum(items=>items.ProductCount)
});
}
In jquery click event I can't define which json is returned. I write if condition as follows but get wrong result.
success: function (data) {
if (data.thumb != null) {//some operations }
else{//some operations }
Perhaps it is very easy problem, but I am new with json. Please help me.
thanks for reply
Check for "undefined" instead
success: function (data) {
if (typeof data.thumb !== "undefined") {//some operations }
else{//some operations }
Because item.ThumbnailPhoto on your server may be null. If this is the case, your check will fail.
Try this,
success: function (data) {
if (data && data.thumb) {//some operations }
else{//some operations }
}
The problem might be because you don't have data.thumbs in your first json, in your Action,
if(true)
{
return Json(new { flag = 1, count = cartItm.ProductCount, total = cartItm.TotalAmount });
}
else
{
return Json(new
{
flag = 2,
thumb = item.ThumbnailPhoto,
productName = item.Name,
itemCount = cartItem.ProductCount,
itemTotal = cartItem.TotalAmount,
productTotal = cart.TotalAmount,
productCount = cart.CartItems.Sum(items=>items.ProductCount)
});
}
in your view :
success: function (data) {
if (data.flag == 1) {//some operations }
elseif (data.flag == 2) {//some operations }
didnt check the code, but this must work.