Static properties in Polymer - polymer

I'm trying to create a service in Polymer but I don't understand how I'm supposed to create static properties.
I would need them to share the same value for all instances ("static") BUT I would also need the observers to be triggered on every instance when they are updated.
I know I could create a variable in the scope of the Polymer() call like this :
var users = [];
Polymer({
is: 'my-service',
properties: {
users: {
type: Array,
value: users,
notify: true,
}
},
// ...
});
But it doesn't meet the second criteria. If I update it from one instance, it doesn't trigger the observers of the other instances.
Do you know how to perform that without triggering the observers on each instance manually ? (I could do it but it would defeat the idea of using a framework in this case.)
EDIT: An alternative would be to create a singleton, but I don't think it's possible.

It is actually quite simple to create static properties with observers. The issue that you are running into is a combination of the design of Polymer and the native implementation of Array.prototype methods.
Arrays and Object observers (deep/path observers) work a little differently than other property observers. In general, notify will only work when the instance changes. Additionally, most Array.prototype.functions return a new instance of the modified array... So, when you splice the array, a different instance is set into the object's property value (not the static property's value) and is now no longer equal to the original static array that you set. This is why only the modifying instance is notified.
My only recommendation to overcome this is: Don't use Array.prototype.splice(), or this.splice(). Even if you do this upon the static property, the result will always be a new array causing incongruous data. From here, you have two options: use Polymer's set API or delete the index and manually notifyPath()
Here is the link that discusses both:
Data binding - Path change notification

<dom-module id="my-service">
<template>
<iron-meta id="my-service-users" value="{{users}}"></iron-meta>
</template>
<script>
Polymer({
is: 'my-service',
properties: {
users: {
type: Array,
value: function(){return [];},
notify: true
}
}
});
</script>
</dom-module>
iron-meta creates a singleton value that automatically updates the users value in all instances, which should also trigger any relevant observers.

Related

Polymer execute complete dom-repeat after immediately dirty checking and assigning

I have an array which contains sub-properties, sub-properties are again objects and sub-arrays.
when I get API response from the server I used to display that using dom-repeat and when I get the another API response of the same format with the minor change in its value, I will replace my old array with the new array with dirty checking like
<template is="dom-repeat" items="{{arr}}">
//paper list codes for display
</temlate>
this.arr = [];
this.arr = newArrayFromResponse;
but when I do this only some paper list will update whose values are changed,
dom-repeat will not create all nodes, it will just update those nodes whose values have changed.
when I just clear array like this.arr = []; in this case, it will destroy all nodes,
but if I immediately reassign once again it will just update nodes.
how to clear all nodes and execute dom-repeat from scratch?
I tried polymers array mutation and render() function and reffered https://github.com/Polymer/polymer/issues/4041 that didn't work!!
each time.
I need to render dom-repeat in a similar way like it's rendering for the first time, like a blank screen and immediate rendering
I think Polymer detects that some values have not changed and there is some performance optimisation happening. You could try setting the new values asynchronously.
this.arr = [];
this.async(function() {
this.arr = newArrayFromResponse;
});
Why does this work and your code does not? To know exactly, we would need to look at Polymer code. My guess is that Polymer does not immediately update the DOM after each assignment to this.arr, but somewhere slightly later. When it does update, in your solution this.arr = newArrayFromResponse has happened and the empty Array is never rendered. Polymer probably still has an copy of the old array around and realises that it is the same array with some new items and just adds the new items. My solution introduces a tiny delay. So Polymer does render the empty array and only later render the new one.

handle a String[] via the PortletPreferences (Liferay6.2)

I have built a MVCPortlet that runs on Liferay 6.2.
It uses a PortletPReferences page that works fine to set/get String preferences parameters via the top right configuration menu.
Now I would need to store there a String[] instead of a regular String.
It seems to be possible as you can store and get some String[] via
portletPreferences.getValues("paramName", StringArrayData);
I want the data to be stored from a form multiline select.
I suppose that I need to call my derived controller (derived from DefaultConfigurationAction) and invoke there portletPreferences.setValues(String, String[]);
If so, in the middle, I will neeed the config jsp to pass the String[] array to the controller via a
request.setAttribute(String, String[]);
Do you think the app can work this way in theory?
If so, here are the problems I encountered when trying to make it work:
For any reason, in my config jsp,
request.setAttribute("paramName", myStringArray);
does not work ->
actionRequest.getAttribute("paramName")
retrieves null in my controller
This is quite a surprise as this usually works.
Maybe the config.jsp works a bit differently than standard jsps?
Then, how can I turn my multiline html select into a String[] attribute?
I had in mind to call a JS function when the form is submitted.
this JS function would generate the StringArray from the select ID (easy)
and then would call the actionURL (more complicated).
Is it possible?
thx in advance.
In your render phase (e.g. in config.jsp) you can't change the state of your portlet - e.g. I wouldn't expect any attributes to persist that are set there. They might survive to the end of the render phase, but not persist to the next action call. From a rendered UI to action they need to be part of a form, not request attributes.
You can store portletpreferences as String[], no problem, see the API for getting and setting them
I think maybe you can use an array in client side, and you can update the javascript array, when user is selecting new values.
So you have the javascript array, then when user click on the action, you can execute the action from javascript also, something like this:
Here "products" is the array with your products.
A.io.request(url, {type: 'POST',
data: {
key: products
},
on: {
success: function(event, id, obj) {
}
}
});
From Action methd you can try to get the parameter with:
ParamUtil.getParameterValues(request,"key");

Polymer function call with parameters

This seems like a very simple one, but somehow I am not sure how to do it.
I want to send data to a Polymer component with the "core-collapse-open" event but this is not working:
<core-collapse on-core-collapse-open="{{loadDetails(data)}}">
{{data.Title}}
...
When I use the above code, the loadDetails function in polymer is not hitting.
Polymer('custom-item', {
data: {},
ready: function () {
},
loadDetails: function (e, details, sender) {
debugger;
}
});
If I am not using the function syntax in the declarative syntax(as below), the loadDetails function hits.
<core-collapse on-core-collapse-open="{{loadDetails}}">
{{data.Title}}
...
How can I send parameters in events.
on-core-collapse-open="{{loadDetails(data)}}"
This means: execute loadDetails(data) and whatever the return value of this is will be bound as the event handler. Not what you want.
Also, the event handler function already receives parameters: the event object. You cannot pass it additional parameters. If the data you want to pass refers to your this.data attribute: the loadDetails function already has access to it in the form of this.data, so you don't need to pass it.
If you're trying to use the same handler function for two different events and pass additional parameters with each individual event: traditionally you'd do that with an anonymous function wrapper, and that's simply not possible using the declarative syntax.
You can bind data to an attribute of core-collapse and then access the data using the target argument of the event handler or the target property of the event argument or alternatively if data is the model of the element anyway just access the TemplateInstance (see full example at https://stackoverflow.com/a/24530099/217408)
I got the answer from my another post. Even though I posted for different context, I understand I can use the same. Thanks Gunter for reply on that thread.
Polymer event parameters on repeat

Does calling ko.mapping.fromJS two time is right thing to do?

I am using knockout mapping plugin to map JSON data to knockout view model. The issue is JSON comes from server data doesn't have all the properties always. But my computed obeservables refer them. So I creates all the observable in first mapping using an empty object(templateStructure) contains all properties and then doing seocond call with actual data to populate the observable with current data. This works fine but want to know that if there any better way to handle the situation?
This is how the two time call is happening right now. templateStructure is dummay object with all the properties and data is actual data.
ko.mapping.fromJS(templateStructure, {}, this);
ko.mapping.fromJS(data, {}, this);
Calling mapping.fromJS to update an existing view model is right. If you're receiving updates to your model using AJAX, it's the easiest way to do it (if you didn'd use mapping, you'd have to do it by hand, property by property).
Your approach of creating a "template viewmodel" with all the properties, so that they exist even if you don't receive it in you JSON responses is good. Besides, it's easier to understand the JavaScript code: you don't need to see the server side to discover which properties are in the view model, as would happen if you made the first mapping directly from the server.
However, if the received model is nearly complete, you can always customize the "create" of your mapping (You could look for missing observable properties using js hasOwnProperty and adding the missing ones). The last example of the docs in the link so precisely how to add a new observable (in this sample, a computed observable):
var myChildModel = function(data) {
ko.mapping.fromJS(data, {}, this); // this is the view model
this.nameLength = ko.computed(function() { // nameLength is added to the vm
return this.name().length;
}, this);
}
In this sample you could add the condition to create nameLength only if not present on the received data, like this:
if (!data.hasOwnProperty('nameLength')) {
/* add the missing observ. property here */
}
(NOTE: you can also customize the update, if needed).
I have solved it using jQuery extend method by merging the object before mapping. So I only needed one call to the mapping function.
var mergedData = jQuery.extend(true,data,templateStructure);
ko.mapping.fromJS(mergedData, {}, this);

Angularjs Directive Delegate not firing through intermediary handler

I have just spent 4 hours trying to implement a directive with a delegate, with no luck.
Use Case:
I have a directive called "filter".
When the user activates/deactivates the filters the parent scope may want to update the data on the screen.
Before I let the parent run, i want to make some internal changes to an internal data structure and pass the new filter state through to the parent.
I have created a jsfiddel to show a simplified version of what i am trying to do.
http://jsfiddle.net/concept/zADNy/
Here is my scope in the directive
scope : {
onFilterChanged : '&'
},
Here is the intermediary handler
function notifyParent() {
scope.onFilterChanged({filters:scope.filters});
}
Directive Delegates are must be lower case (someone please correct me if that statment is wrong, and if so, then why did the camel case version not work)
Ok so after hours of playing and reading and looking at other people's code, i found out that for some reason the delegate functions need to be lowercase.
Here is the resulting fix
http://jsfiddle.net/concept/zADNy/4/
Here is my scope in the directive
scope : {
onfilterchanged : '&'
},
Here is the intermediary handler
function notifyParent() {
scope.onfilterchanged({filters:scope.filters});
}