Polymer databinding confusion with Object Properties - polymer

EDIT - THIS IS A COMPLETE RED HERRING. One of the user properties down the hierarchy had the readOnly property set for user. This was preventing it propagating.
I am struggling to understand databinding and how values propagate when the property changes I have a tree structured set of elements (the structure is spread across separate element definitions - not with <content> tags as possibly implied by the structure show below)
<my-app user="{{user}}">
<my-session user="{{user}}">
<my-login user="{{user}}"></my-login>
</my-session>
<template is="dom-if" if="[[user.name]]">
<my-pages user="{{user}}">
<iron-pages>
<my-menu user="{{user}}"></my-menu>
<my-reports user="{{user}}"></my-reports>
</iron-pages>
</my-pages>
</template>
</my-app>
Each of these elements at their different definitions define a property
user : {
type: Object,
notify: true
}
And all the elements are linked with two way data binding
<my-pages> is lazy loaded using importHref after the user has logged on (and therefore user.name is defined)
I have a property of user called keys which is used for access control. In particular both <my-menu> and a sub element of <my-reports> uses this to determine which menu items to display.
This all works fine on initial log on. But if I change the logged on user, then this change to the user property is apparently not propagating properly
What I can see is that from the debugger triggered during a page change from iron pagess I can see that the <my-app> 's user has the new logged on user value BUT <my-pages>'s user has the old user. For some reason data binding of user is not working down the tree structure, even though it appears to have successfully propagated up from <my-login> to <my-app>. .
I am assuming that possibly the "object" of user is not changing only the paths. I am getting confused about what I should be doing here. Can someone help.

Really cannot tell what's wrong with your code with the information that you have provided except for the syntax error where instead of closing my-pages you have started a new one, but here's a plunker emulating your code. I was able to successfully change the user for all the elements.

Related

Component initialization order in polymer 2

We are migrating a medium sized app from polymer 1 to polymer 3. Thus far we are stuck in the intermediate step of getting our hybrid components to work.
We are encounting some difficulties regarding component initialization timing. For example:
<my-app>
<my-component slot='componentslot'><my-component>
</my-app>
It seems there are cases where my-component is initialized before my-app is initialized. It might vary wether my-component is part of shadow- or light-dom.
We have a lot of tightly coupled components which depend on deterministic initialization order. For example there is a tree-like structure where every edge and every leaf uses events to discover it's own depth in the tree. Therefore we need top-level elements to be initialized before inner components.
But what we found so far was essentially: There is no garantuee for any initialization order of the components.
Is there an established pattern for solving this problem? Will this problem be solved in polymer 3 (so we don't need to care about it anyway)?
Edit
I was asked for some more specific examples
Example 1
<my-layout>
<my-complex-component id="1">
<my-reuseable-part/>
</my-complex-component>
<my-complex-component id="2">
<my-reuseable-part/>
</my-complex-component>
<some-other-component>
<my-reuseable-part/>
</some-other-component>
</my-layout>
I have some reuseable components which need to know if they are inside my-complex-component orsome-other-component. my-complex-component uses a context-discovery-behavior which fires an event containing a callback as payload. my-complex-component and some-other-component have context-behaviors which listen to that event and answer it by invoking the callback.
But as my-reusable-part might be attached before my-complex-component or some-other-component is attached, this pattern does not work.
Registration of event listeners as well as firing the disovering event is done in attached (i.e. connectedCallback).
Example 2
<my-tree>
<my-tree-edge>
<my-tree-edge>
<my-leaf/>
<my-tree-edge>
<my-leaf/>
</my-tree-edge>
</my-tree-edge>
<my-tree-edge>
<my-leaf/>
</my-tree-edge>
<my-leaf/>
</my-tree-edge>
</my-tree>
In the example above every leaf and edge needs to know how deep it is nested. Again every elements fires an event and its parent will answer the event. Again listener registration and event-firing is done in attached/connectedCallback. Again the mechanik fails if an inner node is attached before it's parents are attached.
Hope this helps.
You can use dom-if element if you stricly want to be sure first render my-app then you can let render my-component something like:
<my-app ready="{{myAppReady}}>
<template is='dom-if' if="[[myAppReady]]">
<my-component slot='componentslot'><my-component>
</template>
</my-app>
at my-app script:
static get properties(){return {
ready:{type:Boolean,
notify:true,
value:false
}}
at this part, you may add computed:"checkThisValuesToBeSUre(x,[y]..) in order to be sure if depended to some values or you may add various conditions in order to render my-component
Also, you may import my-component.js dynamically like:
At my-app 's parent script:
static get observers(){return ['_checkMyAppReady(myAppReady)']}
_checkMyAppReady(r){
if(r) import('./my-component.js');
}
EDIT
If there are many elements occurs the same problem, then better to use lazy-import.js:
_checkMyAppReady(r){
if(r) import('./lazy-import.js');
}
lazy-import.js
import './my-component.js';
import './my-component2.js';
import './my-component3.js';
...

Why do we need Polymer Observer if two-way bindng will work?

According to observer, it observes all changes to Polymer properties.
But, two-way binding (using {{}}) already does that, right? Why do we need observer to process the changes?
They are similar in that a value-change invokes the effects of both an observer and data binding, but they have different purposes.
Two-way data binding
A two-way data binding is an annotation
Sets a property of element A to the value of another property in element B
Any changes to B's property automatically update A's property, and vice versa
Observer
An observer is a function that is called whenever a value-change occurs to one or more properties
Example: To set an element's operational mode based on the value of its enabled property
An observer's purpose is not necessarily to set another property (unlike data bindings). It could call another function based on the new property value.
Example: To generate an AJAX request when a url property changes
The observed properties must be in a single element (e.g., a parent element cannot observe a child's property without binding it to its own copy of the property)
I think you got both of them mixed up.
<dom-module id="my-element">
<template>
<paper-input value="{{myValue}}">
</template>
<script>
Polymer({
is:"my-element",
properties:{
myValue:{
type:String
}
}
</script>
</dom-module>
value="{{myValue}}" can be read as whenever there is any change value, myValue will also get updated.
Now, consider a scenario where you want to be informed whenever myValue changes. Above written code is not enough for that (yes, i know you can listen on value-changed event to know about the change, we'll come back to that later). In order to do that you'll need to add observer on myValue only then you'll know when myValue has changed.
Above mentioned case had another solution ie listen to value-changed event fired by paper-input, but what about cases where your property is not binded to any element(its getting its value from db let's say) and you want to know when its value changes.
So to summarise it two-way binding is used when you want to know that value of some property which is not part of your own shadow-dom has changed and observer is used when you want to know about the changes in properties inside your own dom

Dom-Repeat Render: What is it actually do? Proper way to tell dom-repeat re-render its item?

I was fighting with a nested dom-repeat for the last 2 days.
I somewhat now know how to re-render when sub-properties changed.
But still don't understand the purpose of the render function.
I changed the data binded to a dom-repeat, and call the render, but nothing happens.
_renderDREmployees: function() {
this.employees[0].name="RENDERED"
// Why this doesn't work ?
this.$.drEmployees.render()
},
See here
http://plnkr.co/edit/Y0P5vNxg46t5fX7gJFxU?p=preview
Could somebody explain to me please?
If you could take a look of my example, to see if I do it the right away.
What could be other ways/proper way to re-render item inside a dom-repeat when the data that binded to it was changed outside.
Thank you
In order to mutate object properties in Polymer, you must use the object mutation methods in order for this changes to propagate inside your element.
Hence,
this.employees[0].name="RENDERED"
should be
this.set('employees.#0.name', 'RENDERED')
render only picks up observable changes such as those made with Polymer's array mutation methods.
Here is example where it works(I mean on name="RENDERED"): Plunk
Right way to do it is:
this.set('employees.0.name', "RENDERED set");
Docs Polymer array-binding

How can I add an additional binding to an element inside a dom-repeat?

I have a template dom-repeat element. I know that I can define the items attribute from an array, so that item can be accessed inside the template. However, I don't know how to access other objects inside the template if I bind them to customer polymer elements.
In this example, items is being defined by someItems, and item is passed into the element <my-el>. I also have a string mine that I want to pass into <my-el> and use there. I currently have the idea to do this in app-el.html, which contains the dom-repeat template:
app-el.html
<template is="dom-repeat" items="[[someItems]]">
<my-el item=[[item]] mine="[[mine]]"></my-el><br>
</template>
In theory, I would be able to access both in my-el.html like this:
my-el.html
[[item]] [[mine]]
However, when I try to access mine from inside <my-el> it is undefined. How can I correctly pass in this string so I can access it?
An MCVE can be found on this Plunker. Note how the item string is defined inside <my-el> but the mine string is not.
Your data bindings look correct, but there is no mine property for my-app. Did you mean to define <my-app>.myProperty as <my-app>.mine? Changing that property name to mine fixes your problem.
plunker

Confused about the difference between Polymer attribute reflection and two way databinding

I am trying to make a polymer element that authenticates users against an SMF forum. I want the element to expose a "user" property which will initially be an empty object {} but if/when the element has made an ajax request and is able to authenticate the user, this object will contain details about the user (and an authentication token) for use in the rest of the application. The rest of the application is in the content section of the element, and will only be displayed when authetication has happened.
So the application is structured like this
<smf-auth login="login/url", fail="fail/url", splash="initial/splash/img" user>
rest of application which will need access to user
</smf-auth>
I have published user with reflective properties, and set its initial value to {}. However when I run unit tests, I run javascript to get the element (as a javascript variable) and look at el.user and it has the value "" (ie blank string). I tried altering the code to initialise user as something more complex, but I still get a blank string.
Here is the element definition (with my more complex user)
<polymer-element name="smf-auth" attributes="login fail splash">
<template>
<core-ajax id="req" url="{{login}}" handleAs="json" on-core-response="{{handleResponse}}"></core-ajax>
<img id="splash" src="{{splash}}"/>
<template if="{{autheticated}}">
<content></content>
</template>
</template>
<script>
Polymer('smf-auth',{
publish:{
user:{
value:{token:{},uid:0,name:''},
reflect:true
}
},
created:function(){
this.authenticated = false;
},
attached:function(){
this.$.req.go();
},
handleResponse:function(response){
//TODO decide what the response is
}
});
</script>
</polymer-element>
So how should I declare and use the "user" property so that the content of the element (more polymer elements) has access to it
You need to specify a data binding to a user property when you instantiate the smf-auth element. Then you can access this property inside the element's body:
<smf-auth user="{{user}}" ...>
Hello {{user.name}}!
</smf-auth>
This assumes that your smf-auth element is itself inside a Polymer element. Otherwise you need an auto-binding template element.
If your element hierarchy is deep and you need the user object in an element at the bottom, it can be a problem to pass the user property down the hierarchy. In this case another option might be to use core-signals and send an event when the user has logged in. Inner elements can then listen for this event and fetch the user object from the event details.