Updating Polymer Component with data from outside the component - socket.io

This should be a pretty normal task , and yet i am missing something .
I am trying to integrate Socket.io with Polymer [ using the chat application ] - Deciding to change the MessageList and the individual messageItem as Polymer components .
SocketIo exposes a customEvent to be thrown from server , which sends the message as data , which then is being assigned to a property on the custom element .
This is the MessageList element .
<link rel="import" href="/polymer/polymer.html">
<link rel="import" href="message.html">
<dom-module id='message-list'>
<template>
<style>
</style>
<ul id="messages">
<template is='dom-repeat' items="{{messageList}}" is="auto-binding">
<li>
<message-item message = "{{item}}"></message-item>
</li>
</template>
</ul>
</template>
<script>
var messageListElement = Polymer({
is : 'message-list',
properties : {
messageList : {
type : Array,
observer: '_messageListChanged',
reflect : true ,
value : function() {
return [{'inputMessage' : 'Welcome to the Chat' ,
'nickName' : 'System' , 'msgTime' : new Date()}]
}
//, notify : true
}
},
_messageListChanged: function(newValue , oldValue) {
console.log("Data changed");
},
created : function() {
console.log("messagelist created");
},
ready : function() {
console.log("messagelist ready");
},
attributeChanged : function() {
console.log("messagelist attributeChanged");
}
});
</script>
On the index.html Page -
var self = this;
socket.on('chatMessage' , function(msg) {
self.messages.push(msg);
console.log(self.messages);
document.querySelector('message-list').messageList = self.messages;
});
With all of this.. Anytime a client sends a message , the self.messages - posts the total set of messages , but the "_messageListChanged" of the custom elements gets called only the first time .
There are similar questions - Updating a polymer element property with data from API call
However assigning the data , works only for the first time .
Also i would like to be able to do it without using ajax-iron and stuff .

In addition to using the Polymer API for array mutations (as Alon has pointed out in his answer), you need to install an observer for array mutations. The observer you have now will only fire when you assign a new array instance, but not when you add or remove elements from your array.
properties : {
messageList : {
type : Array,
value : function() {
return [{'inputMessage' : 'Welcome to the Chat' ,
'nickName' : 'System' , 'msgTime' : new Date()}]
}
}
},
observers: [
'_messageListChanged(messageList.splices)'
],
Note that this kind of observer takes a single argument, a change record.
Your observer method should accept a single argument. When your observer method is called, it receives a change record of the mutations that occurred on the array.

The problem is that you use push. polymer has his own push function.The regular push does not trigger the observers, and all the change events.
this.push('array',value)
But for you, you can solve it like this:
var self = this;
socket.on('chatMessage' , function(msg) {
self.messages.push(msg);
document.querySelector('message-list').messageList = self.messages.slice();
});
The slice will create new array. And it will trigger all the change in the array.

please try adding reflectToAttribute:true to your messageList property.
if you still don't get it solved try to so this
self.myimmutedArray JSON.parse(JSON.stringify(self.messages))
before document.querySelector('message-list').messageList = self.myimmutedArray;
reason could be the javascript array push or slice will not refelect in polymer's dirty checking
if you use below code in the a polymer component. you must follow polymer's array changes like
this.push('messages', {title:'hey'});
var self = this;
socket.on('chatMessage' , function(msg) {
self.messages.push(msg);
console.log(self.messages);
document.querySelector('message-list').messageList = self.messages;
});

Related

How do I pass custom values to CKFinder3 when instantiating a CKEditor4 instance?

I'm having some trouble using pass to pass variables to my CKFinder3 (ASP) connector when using CKEditor4.
I create my editor instance with:
CKFinder.setupCKEditor( myEditor, {
pass: 'testVar',
testVar: 'nooice',
...
});
but the variable just doesn't seem to make it over to CKFinder.
If I add this code to the CKFinder config directly it does work though:
config.pass = 'testVar';
config.testVar = 'nooice';
That's great, but the values I want to pass will be dynamic, so I need to pass them in when I call .setupCKEditor() on the page. I've also tried using connectorInfo: 'testVar=nooice', but that doesn't work either.
Has anyone run into this? I found a great answer and example on this question, How to pass query string parameters in ckeditor for the picture (ckfinder) button?, but the described solution is basically what I'm doing and has no affect for me.
I have been able to get this working in a CKEditor5 test using:
ClassicEditor.create( document.querySelector( '#bodyContent' ), {
ckfinder: {
uploadUrl: '/ckfinder3/connector?command=QuickUpload&type=Images&responseType=json',
options: {
pass: 'testVar',
testVar: 'nooice'
}
},
...
} );
But I cannot figure it out in CKEditor4.
You pass them like so:
var editor = CKEDITOR.replace( 'editor1', {
language : 'en',
} );
CKFinder.setupCKEditor( editor, {
test : 'testvalA',
token : '7901a26e4bc422aef54eb45A',
pass : 'token,test'
});
In the example above you are passing test and token parameters.
If you are using manual integration method, you need to attach parameters to filebrowserXYZBrowseUrl configuration settings as shown below:
var editor = CKEDITOR.replace( 'editor1', {
filebrowserBrowseUrl: '../ckfinder/ckfinder.html?id=abc&foo=bar&test=custom',
filebrowserImageBrowseUrl: '/ckfinder/ckfinder.html?type=Images&id=abc&foo=bar&test=custom',
filebrowserUploadUrl: '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files&id=abc&custom=test',
filebrowserImageUploadUrl: '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images&id=abc&custom=test',
} );
Now the problem is that CKFinder will only pass a predefined set or URL parameters: id, type, resourceType, langCode, CKEditor and CKEditorFuncNum. If you would like to use more parameters, you need to pass them manually as CKFinder configuration settings and you need to do that in ckfinder/ckfinder.html file (you need to modify it) e.g.
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>CKFinder 3 - File Browser</title>
</head>
<body>
<script src="ckfinder.js"></script>
<script>
function getUrlParams() {
var vars = {};
window.location.href.replace( /[?&]+([^=&]+)=([^&]*)/gi, function( match, key, value ) {
vars[ key ] = value;
} );
return vars;
}
var params = getUrlParams(),
config = { pass : '' },
ckfServicedParams = [ 'id', 'type', 'resourceType', 'langCode', 'CKEditor', 'CKEditorFuncNum' ];
for( var key in params ){
if ( ckfServicedParams.indexOf( key ) < 0 ) {
config.pass = config.pass.concat( config.pass ? ','+key : key);
config[key] = params[key];
}
}
CKFinder.start( config );
</script>
</body>
</html>
NOTES:
If you would like extra parameters to be sent when you upload files using CKEditor Image Dialog Upload Tab, you need to add them to filebrowserXYZUploadUrl configuration settings as well (you can use different parameters as shown in example above).
Please note these parameters aren't exactly dynamic. You define them once per editor load and can't change them afterwards unless you destroy/create editor instance or reload page with the editor.

Magento2 Knockout.js contentUpdated not binding observables after ajax.

I have an issue with observables being fired after content is updated via ajax and javascript is re-initialized via .trigger('contentUpdated'). This all works when the scripts are rendered initially on page load but when they are added via ajax I cannot get the observables to update. For demo purposes I've simplified by logic but basically I have a block that gets loaded via ajax for a product collection like so:
myblock.phtml
<div class="wrapper-id-1">
<!-- this is what gets appended via ajax
<div id="product-item-<?php echo $productId;?>">
<span data-bind="text: someObservable()"></span>
...
</div>
<script type="text/x-magento-init">
{
"#product-item-<?php echo $productId;?>": {
"path/to/component":{
"some":"vars"
}
}
</script>
<!--and ajax append ends here -->
</div>
In the component that gets bound to the element I have:
component.js
...
this.someObservable: ko.observable('default value'),
initialize: function () {
var self = this;
this.anotherComponentModel().value.subscribe(function(data){
self.someObservable(data['value']);
},this);
},
...
the ajax that calls and loads the collection:
ajaxComponent.js
$.ajax({
url: 'route/to/controller?cat=' + categoryId,
success: function (data) {
$('.wrapper-id-' + categoryId).empty().append('<h2>' + categoryTitle + '</h2>' + data).show().trigger('contentUpdated');
}
})
...
I see that the component(component.js) gets initialized when contentUpdated is triggered and it has all of the correct data that is needed. However the observables to not fire and the data is not updated to the DOM. This an issue with scope? Or to I need to re-initialize the observables? I tried doing this via not binding directly to the component ie:
<script type="text/x-magento-init">
{
// components initialized without binding to an element
"*": {
"<js_component3>": ...
}
}
</script>
but it achieves the same result.
What am I missing here.

Javascript code end of webpage with Spring and Dojo

I have a pretty specific issue to deal with : I am looking for a solution about IE6 crashing when there is too much javascript in a webpage. The project I am working on is using Dojo, SpringJS, Apache Tiles and Spring Webflow. For each field used (defined in .tagx files), the decoration is added as following :
<script type="text/javascript">
Spring.addDecoration(
new Spring.ElementDecoration({
elementId : '_${field}_id',
widgetType : 'dijit.form.ValidationTextBox',
widgetAttrs : {
<!-- Widget attrs -->
}
})
);
</script>
So, in the generated webpage, a lot of javascript is added everywhere. The problem is IE6 seems to crash when there is too much javascript. The solution "experts" found was to write all the javascript code at the end of the HTML page.
In intent to do that, I tried to create a new tag called putScriptInCache.tagx :
<jsp:useBean id="mapScripts" class="java.util.HashMap" scope="request"/>
<jsp:doBody var="theBody" />
<c:set target="${mapScripts}" property="${id}" value="${theBody}"/>
Which replaces previous javascript tag :
<lbputil:putScriptInCache id="${field}">
Spring.addDecoration(
new Spring.ElementDecoration({
elementId : '_${field}_id',
widgetType : 'dijit.form.ValidationTextBox',
widgetAttrs : {
<!-- Widget attrs -->
}
})
);
</lbputil:putScriptInCache>
Finally, I have written a piece of code which loops on the map created and write javascript at the end of the html body :
<script type="text/javascript">
dojo.addOnLoad(function(){
<c:forEach items="${mapScripts}" var="script">
<c:out value="${script.value}" escapeXml="false" />
</c:forEach>
});
</script>
It seems to work pretty well but an issue remains : when an ajax request is fired, an Apache Tiles fragment of the jsp is reloaded using Spring Webflow. After that, I noticed that the map in request scope was empty and I can't figure out why. It should have been filled with the reloaded fragment fields javascript code.
EDIT : If someone has a totally different way to solve my initial issue, do not hesitate to propose it !
After lookep at the spring-dojo.js, I found that this script already evaluates scripts at the end of the fragment rendering.
handleResponse: function(response, ioArgs, callbackResponse) {
//...
//Extract and store all <script> elements from the response
var scriptPattern = '(?:<script(.|[\n|\r])*?>)((\n|\r|.)*?)(?:<\/script>)';
var extractedScriptNodes = [];
var matchAll = new RegExp(scriptPattern, 'img');
var matchOne = new RegExp(scriptPattern, 'im');
var scriptNodes = response.match(matchAll);
if (scriptNodes != null)
{
for (var i=0; i<scriptNodes.length; i++)
{
var script = (scriptNodes[i].match(matchOne) || ['','',''])[2];
script = script.replace(/<!--/mg,'').replace(/\/\/-->/mg,'').replace(/<!\[CDATA\[(\/\/>)*/mg,'').replace(/(<!)*\]\]>/mg,'');
extractedScriptNodes.push(script);
}
}
//...
//Evaluate any script code
dojo.forEach(extractedScriptNodes, function(script){
dojo.eval(script);
});
// APPEL du callBackResponse
if (callbackResponse != null){
callbackResponse();
}
return response;
},
First, it stores script tags into extractedScriptNodes, then it replaces the script tags with // Original script removed to avoid re-execution . Finally, it evaluates each extractedScriptNodes after having rendered the view.
So, it should already work...

Keeping data in-sync with server

This is my stack : Ember.js + Express/Node.js
Say i have an Endpoint as \posts, it will return an array of objects.
and i have following template named allPosts :
{{#each post in content}}
<p>{{post.body}} </p>
{{/each}}
Route:
App.AllPosts =Ember.Object.extend({
body : null
})
App.AllPostsRoute = Ember.Route.extend({
setupController : function(controller,model){
controller.set('content',model);
}
});
And controller as
App.AllPostsController = Ember.Controller.extend({
actions: {
save : fucntion(){
// Get And update data from server via ajax
}
}
});
I want to keep data in sync with data on server, for this i planned to use setInterval and call the save action above every 1000ms to update the data. But it doesn't work. i used setInterval like this
setInterval(App.AllPostsController.actions.save,3000);
I DONT want to use Ember Data. As the data is dependent on another Node app which runs server side.
You're trying to run an action on a type, not an instance of the controller. Instead you should start saving when you actually hit the route and controller, setupController is a good place to accomplish this.
App.AllPostsRoute = Ember.Route.extend({
setupController : function(controller,model){
controller.set('content',model); // in this code model would be blank, I'm assuming you're leaving out code
this.startSaving(controller);
},
willTransition: function(transition){
//if I'm leaving, this.stopSaving();
},
isSaving: false,
startSaving: function(controller){
this.set('isSaving', true);
this.realSave(controller);
},
realSave: function(controller){
if(!this.get('isSaving')) return;
Em.run.later(function(){
controller.send('save');
}
},
stopSaving: function(){
this.set('isSaving', false);
}
});

Ember event in one view update another?

I have a small extract from my Ember app here. My page contains a number of views each containing different data each with their own controllers.
I want a search field (in index view) to go in one view which should "talk" to the stationList controller to update the content of the stationList view. This doesn't work. I get an error: TypeError: this.get(...).search is not a function
The logging outputs the name of the contoller I've asked it to use: App.StationListController
I added a second search form inside on the StationList View. This one works just fine. The logging this time outputs a dump of the StationListController object. So I am guessing that the other search form, despite my code (in SearchFormView): controllerBinding : 'App.StationListController', is not correctly setting the controller.
So I guess my question is why not?
How can I route the change on the form field in the one view to call a funciton on another view's controller?
Here's my code:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<div id="searchForm">search form view search:
{{#view App.SearchFormView}}
{{view App.StationSearchField}}
{{/view}}
</div>
<div id="stationList">{{render stationList}}</div>
</script>
<script type="text/x-handlebars" data-template-name="stationList">
station list view search: {{view App.StationSearchField}}
<ul>
<li>List</li>
<li>will</li>
<li>go</li>
<li>here</li>
</ul>
{{searchTerm}}
</script>
And
App = Ember.Application.create({})
App.SearchFormView = Ember.View.extend({
init : function()
{
console.log("SearchFormView init", this.get('controller'))
}
})
App.StationSearchField = Ember.TextField.extend({
keyUp: function(event) {
var searchTerm = this.value
console.log("value",searchTerm,this.get('controller'))
this.get('controller').search(searchTerm)
}
})
App.StationListController = Ember.ArrayController.extend({
content : [],
searchTerm : null,
search : function(term)
{
this.set("searchTerm",term)
console.log("searching",term)
}
});
Fiddle: http://jsfiddle.net/ianbale/8QbrK/14/
I think the controllerBinding stuff is from the older version, I don't think that works anymore.
You can use controllerFor on get('controller') in the StationSearchField.
this.get('controller').controllerFor('station_list').search(searchTerm)
But controllerFor is deprecated and may be removed. Depending on your application structure you use needs on the controller.
Another way which I am using, is to send a custom event from the View, which the Route then sends to the corresponding controller.
App.IndexRoute = Ember.Route.extend({
events: {
search: function(term) {
controller = this.controllerFor('station_list')
controller.search(term);
}
}
});
and dispatch a search event from view like so.
this.get('controller').send('search', searchTerm);
The advantage of this method is you dispatch the same event from multiple places and it would get handled in the same way.
Here's the updated jsfiddle.

Resources