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.)
Related
This is my code to get the Object which is returning but I cannot open that object I want to open and see whats in it
var connections = [];
$.each(instance.getAllConnections(), function (idx, connection) {
connections.push({
connectionId: connection.id,
pageSourceId: connection.sourceId,
pageTargetId: connection.targetId,
anchors: $.map(connection.endpoints, function(endpoint) {
return [[endpoint.anchor.x,
endpoint.anchor.y,
endpoint.anchor.getOrientation()[0],
endpoint.anchor.getOrientation()[1],
endpoint.anchor.offsets[0],
endpoint.anchor.offsets[1]
]];
})
});
console.log(util.inspect(connections.endpoints));
});
// console.log(connections);
console.log(util.inspect(connections));
This is what I am getting in the Object
I got the Object by doing this
var connections = [];
$.each(instance.getAllConnections(), function (idx, connection) {
connections.push({
connectionId: connection.id,
pageSourceId: connection.sourceId,
pageTargetId: connection.targetId,
arrowendpoint1:connection.endpoints[0].anchor.x,
arrowendpoint2:connection.endpoints[0].anchor.y,
arrownendpoint3:connection.endpoints[1].anchor.x,
arrownendpoint4:connection.endpoints[1].anchor.y,
// anchors: $.map(connection.endpoints, function(endpoint) {
// return [[endpoint.anchor.x,
// endpoint.anchor.y,
// endpoint.anchor.getOrientation()[0],
// endpoint.anchor.getOrientation()[1],
// endpoint.anchor.offsets[0],
// endpoint.anchor.offsets[1]
// ]];
// })
});
});
console.log(util.inspect(connections));
I have two functions in the same TypeScript component. When I try to call one already declared, VSCode reports that it "[ts] Cannot find name 'XXX'.".
As requested by Tiep Phan, this is the full code:
liveSearchFMs(input: any) {
this._ectmService.getFMsFromUserSearch(input).subscribe(
fanmissions => this.fanmissions = fanmissions,
error => this.errorMessage = <any>error
);
}
timeout(input) {
var enteredValue = input;
var timeout = null;
clearTimeout(timeout);
timeout = setTimeout(function () {
this.liveSearchFMs(enteredValue);
}, 1000);
}
I guess you wanna create something like this
export class EctmListComponent implements OnInit {
// other code
private timeoutTracker;
timeout(input) {
if (this.timeoutTracker) {
clearTimeout(this.timeoutTracker);
}
//use arrow function instead
this.timeoutTracker = setTimeout(() => {
this.liveSearchFMs(input);
}, 1000);
// or store context first
/*
const ctx = this;
this.timeoutTracker = setTimeout(function() {
ctx.liveSearchFMs(input);
}, 1000);
*/
// or using bind method
/*
this.timeoutTracker = setTimeout((function() {
this.liveSearchFMs(input);
}).bind(this), 1000);
*/
}
}
You need to use this keyword. so this.liveSearchFMs
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.
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)));
}
})
}
Ok, so I have this situation here:
A CSHTML view with a kendo tree in it:
#(Html.Kendo().TreeView()
.Name("treeview")
.DataTextField("Name")
.DataSource(d => d.Read(r => r.Action("WorkedHours", "TaskManager")))
.Events(e => e.Select("onSelect"))
)
to the right of that there is a kendo grid. and above the tree there is a (kendo) dropdown list to select a user.
this is the controller method called by the tree:
public JsonResult WorkedHours(uint? id)
{
DocObjectArray docObjects = null;
if (id == null)
{
// get root elements
var loggedInUserRef = OmanagerUtils.GetInstance().LoggedInUser;
if (loggedInUserRef != null && loggedInUserRef.GetObject() != null && loggedInUserRef.GetObject().SubObjects != null)
{
for (int i = 0; i < loggedInUserRef.GetObject().SubObjects.GetLength(); i++)
{
var item = loggedInUserRef.GetObject().SubObjects.GetAt(i);
if (item.ToString() == TaskManagerConstants.UserWorkHours)
{
docObjects = item.TreeSubObjects;
break;
}
}
}
}
else
{
// get sub objects of a root object
var rootObj = new DocObjectRef((int)id);
docObjects = rootObj.GetObject().TreeSubObjects;
}
var returnDocObjects = new List<OmanagerItem>();
for (int i = 0; i < docObjects.GetLength(); i++)
{
var item = docObjects.GetAt(i);
var hasChildren = true;
if (item.TreeSubObjects == null)
{
hasChildren = false;
}
else
{
if (item.TreeSubObjects.GetLength() == 0)
{
hasChildren = false;
}
}
var listItem = new OmanagerItem
{
hasChildren = hasChildren,
id = item.GetOID(),
Name = item.ToString()
};
returnDocObjects.Add(listItem);
}
return Json(returnDocObjects, JsonRequestBehavior.AllowGet);
}
now, the problem is that i have to be able to select a user from the dropdown list and refresh the tree with this new data.
$("#employee").kendoDropDownList({
change: function () {
var postdata = {
id:$("#employee").val()
}
$.ajax({
url: "TaskManager/WorkedHours",
cache: false,
type: "POST",
data: postdata,
success: function (data) {
$("#treeview").data("kendoTreeView").setDataSource(data);
},
});
}
});
the problem is what do i do with this data? because my attempt did not really work.
many thanks.
You can use OutputCache attribute on WorkedHours action:
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
public JsonResult WorkedHours(uint? id)
{
// rest of method
}
It helped in my case :)
Maybe this little snippet is of any help to you.
Similar to your code in the change event of my dropdown I'm calling a function that will change the request data of my TreeView DataSource.
After changing it, it calls the read() handler of the datasource so it re-reads the data:
function loadTreeViewData() {
var employee = $('#employee').getKendoDropDownList().dataItem();
WorkedHoursDataSource.transport.options.read.data = {Employee_Id:employee.id};
WorkedHoursDataSource.read();
}