Remove child element's attribute from Polymer js - polymer

I've a custom element which, among other things, has a core-input and a paper button in it.
When the element is created, the input is disabled, and I want to enable it when I tap the button.
I've tried several ways and can't access the input's attribute.
<paper-input-decorator label="Nombre de usuario" floatingLabel>
<input id="usernameinput" value="{{UserName}}" is="core-input" disabled />
</paper-input-decorator>
<paper-button raised id="edprobutton" on-tap="{{edbutTapped}}">EDITAR</paper-button>
What should I write in
edbutTapped: function () {
},
EDIT
So, I've learned that the problem was that my username input element was inside a repeat template, and that's bad for what I was trying to do. Now I'm trying to bind a single json object to my element, with no luck so far.
What I have right now:
In my Index page:
<profile-page id="profpage" isProfile="true" entity="{{profEntity}}"></profile-page>
<script>
$(document).ready(function () {
var maintemplate = document.querySelector('#fulltemplate');
$.getJSON('api/userProfile.json', function (data) {
var jsonString = JSON.stringify(data);
alert(jsonString);
maintemplate.profEntity = jsonString;
});
}
</script>
In my element's page:
<polymer-element name="profile-page" attributes="isprofile entity">
<template>
<style>
[...]
</style>
<div flex vertical layout>
<core-label class="namepro">{{entity.Name}}</core-label>
<core-label class="subpro">{{entity.CompanyPosition}}</core-label>
<core-label class="subpro">{{entity.OrgUnitName}}</core-label>
</div>
</template>
</polymer-element>
And my JSON looks like this:
{"Name": "Sara Alvarez","CompanyPosition": "Desarrollo","OrgUnitName": "N-Adviser"}
I'm asuming I need to "update" my element somehow after changing its entity attribute?

Try the following
<script>
Polymer({
edbutTapped: function () {
this.$.usernameinput.disabled = false;
}
});
</script>
The this.$ allows you to access controls defined in an elements and the usernameinput is the id you assigned to the input.
This can go below the closing tag of the element you are defining.

'disabled' is conditional-attribute.
So this will be the correct use of it:
<input id="usernameinput" value="{{UserName}}" is="core-input" disabled?="{{isDisabled}}" />
In the prototype:
//first disable the field, can be done in ready callback:
ready: function () {
this.isDisabled = 'true';
}
//set idDisabled to 'false' i.e. enable the input
edbutTapped: function () {
this.isDisabled = 'false';
},

OK this is going to be a long answer (hence why I am not entering this as an edit of my original answer). I've just done something which is functionally the same.
The first thing is this code;
$.getJSON('api/userProfile.json', function (data) {
var jsonString = JSON.stringify(data);
alert(jsonString);
maintemplate.profEntity = jsonString;
});
Polymer has a control called core-ajax - this as it's name suggests makes an ajax call. The other really nice thing is that it can be made to execute when the URL changes. This is the code from the project I've got.
<core-ajax id="ajax"
auto=true
method="POST"
url="/RoutingMapHandler.php?Command=retrieve&Id=all"
response="{{response}}"
handleas="json"
on-core-error="{{handleError}}"
on-core-response="{{handleResponse}}">
</core-ajax>
The auto is the bit which tells it to fire when the URL changes. The description of auto from the polymer documentation is as follows;
With auto set to true, the element performs a request whenever its
url, params or body properties are changed.
you don't need the on-core-response but the on-core-error might be more useful. For my code response contains the JSON returned.
So for your code - it would be something like this
<core-ajax id="ajax"
auto=true
method="POST"
url="/api/userProfile.json"
response="{{jsonString}}"
handleas="json"
on-core-error="{{handleError}}" >
</core-ajax>
Now we have the data coming into your project we need to handle this. This is done by making use of Polymer's data-binding.
Lets detour to the element you are creating. Cannot see anything wrong with the following line.
<polymer-element name="profile-page" attributes="isprofile entity">
We have an element called 'profile-page' with two properties 'isprofile' and 'entity'.
Only because my Javascript leaves a bit to be desired I would pass each property as a seperate entity making that line
<polymer-element name="profile-page" attributes="isprofile name companyposition OrgUnitName">
Then at the bottom of your element define a script tag
<script>
Polymer({
name: "",
companyposition: "",
OrgUnitName: ""
});
</script>
Now back to the calling (profile-page). The following code (from my project) has the following;
<template repeat="{{m in response.data}}">
<map-list-element mapname="{{m.mapName}}" recordid="{{m.Id}}" on-show-settings="{{showSettings}}">
</map-list-element>
</template>
Here we repeat the following each element. In your case you only have one entry and it is stored in jsonString so your template is something like this
<template repeat="{{u in jsonString}}">
<profile-page name="{{u.name}} companyposition="{{u.companyposition}}" OrgUnitName="{{u.OrgUnitName}}">
</profile-page>
</template>
Now we get to the issue you have. Return to your profie-page element. Nothing wrong with the line
on-tap="{{edbutTapped}}"
This calls a function called edbutTapped. Taking the code I gave you earlier
<script>
Polymer({
edbutTapped: function () {
this.$.usernameinput.disabled = false;
}
});
</script>
The only thing to change here is add the following code
created: function() {
this.$.usernameinput.disabled = true;
},
This is inserted after the Polymer({ line. I cannot see in your revised code where the usernameinput is defined but I am assuming you have not posted it and it is defined in the element.
And you should be working, but remember to keep your case consistent and to be honest I've not been - certain parts of Polymer are case sensitive - that catches me out all the time :)

Related

How to change parent element property from child element?

How can I let a child element change the value of a property in the parent element with the ability to observe that change in the parent element
<link rel="import" href="paper-tree-node.html">
<dom-module id="paper-tree">
<template>
<div>
<paper-tree-node id="root" data="[[data]]" actions="[[actions]]" on-click='_handlePaperCheck' chapterIds={{chapterIds}}></paper-tree-node>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'paper-tree',
properties: {
chapterIds: {
type: Array,
value: [],
notify: true,
observer: "_chapterChanged"
}
},
_handlePaperCheck: function (e) {
let element = e.target.parentElement
if (element.checked) {
this.push('chapterIds', parseInt(element.id.substr(2)))
// console.info(this.chapterIds);
} else {
var index = this.chapterIds.indexOf(element.id);
this.splice('chapterIds', index, 1)
// console.info(this.chapterIds);
}
},
_chapterChanged: function () {
console.log(this.chapterIds)
// this.$.root.chapterIds = this.chapterIds
}
})
noting that paper-tree-node is a child element hosts a paper-check inside it's template, the purpose of this is to harvest the clicked paper-tree-node id attr and push it to the chapterIds property.
Problem is that _chapterChanged wont fire when i click on any checkbox
I am attaching a sample project since this cannot be posted on somthing like jsbin, here is a gdrive zip folder for the project https://drive.google.com/file/d/1yCeXkZu8Yp-8GUgadGHIfeP5w5uyI12J/view?usp=sharing
You're using the right thinking, but not the entire way.
notify: true, should be stated in your child element paper-tree-node under the property chapterIds, and not under your paper-tree element. I made this mistake too when I began with Polymer.
Also, whenever Polymer sees camelCase variables, it assumes the variable contains dashes:
<paper-tree-node id="root" data="[[data]]" actions="[[actions]]" on-click='_handlePaperCheck' chapterIds={{chapterIds}}></paper-tree-node>
... should be ...
<paper-tree-node id="root" data="[[data]]" actions="[[actions]]" on-click='_handlePaperCheck' chapter-ids={{chapterIds}}></paper-tree-node>
... where I switched the property chapterIds to chapter-ids. I rarely use camelCase variables when creating a new element because this mistake is so easy to make.
You can do this with an event or with data binding.

Two way data binding with Polymer.Templatizer

I am trying to get two way data-binding between a host element and a template in Polymer using templatizer. For example if I am trying to keep two input boxes in-sync:
<html>
<body>
<my-element>
<template >
<input type="text" value="{{test::change}}" />
<div>The value of 'test' is: <span>{{test}}</span></div>
</template>
</my-element>
<dom-module id="my-element">
<template>
<input type="text" value="{{test::change}}" />
value:
<p>{{test}}</p>
<div id="items"></div>
<content id="template"></content>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
test: {
type: String,
value: "a"
},
behaviors: [ Polymer.Templatizer ],
_forwardParentProp: function(prop, value) {debugger},
_forwardParentPath: function(path, value) {debugger},
_forwardInstanceProp: function(inst, prop, value) {debugger},
_forwardInstancePath: function(inst, path, value) {debugger},
ready: function() {
this._instanceProps = {
test: true
};
var templates = Polymer.dom(this.$.template).getDistributedNodes();
template = templates[1];
this.templatize(template);
var itemNode = this.stamp({ test: this.test});
Polymer.dom(this.$.items).appendChild(itemNode.root);
}
});
</script>
</body>
</html>
In the above code I hit the debugger in the _forwardInstanceProp but not any of the others. Why is this? Inside _forwardInstanceProp I can access my-element and manually update the test property. Is there a better way to do this? I also could add an observer on my-element to the test property and then propagate any changes in my-element to the template. Is there a better way to do that? I am just trying to understand what all four of these methods do and when/why they should be used.
It beats my why I can never get neither _forwardParentPath nor _forwardParentProp to run. However, I know when the other two run :)
_forwardInstanceProp runs for direct properties of model passed to stamp and _instanceProps is initialized:
this._instanceProps = {
text: true
};
var clone = this.stamp({
text: this.text
});
_forwardInstancePath on the other hand runs when you pass nested objects to stamp:
var clone = this.stamp({
nested: {
text: this.text
}
});
See this bin for an example: http://jsbin.com/kipato/2/edit?html,js,console,output
In the stamped template there are two inputs bound to two variables which trigger instanceProp and instancePath. Unfortunately I've been unable to fix the error thrown when the latter happens.

Set empty attribute to polymer component inside another one

I've been trying to set an empty attribute to a polymer element inside another one.
This is the code for my custom element. What I'm trying to do is set "required" to the input element whenever custom-core-input's attribute required is true.
<polymer-element name="custom-core-input" attributes="columnId inputError validation required">
<template>
<section>
<paper-input-decorator id="decorator" error="{{inputError}}">
<input id="custominput" is="core-input" validate="{{validation}}" on-change="{{inputCommited}}">
</paper-input-decorator>
</section>
</template>
<script>
Polymer({
inputCommited: function () {
this.$.decorator.isInvalid = !this.$.custominput.validity.valid;
}
});
</script>
So far I've tried accessing the input element and set 'attr' from the script, I thought it wouldn't work, but was worth the try. I just have no idea about how to approach this, I feel like there must be an easy answer but cant think of it.
Also (and unrelated), I think I'm doing something else wrong, since the paper-input-decorator won't 'take' the inputError value.
Thanks for reading :)
Required is a conditional attribute, so you can set it on the input element like so:
<polymer-element name="custom-core-input"
attributes="columnId inputError validation required">
<template>
<section>
<paper-input-decorator id="decorator"
error="{{inputError}}"
autovalidate>
<input id="custominput" is="core-input"
validate="{{validation}}"
on-change="{{inputCommited}}"
required?="{{required}}">
</paper-input-decorator>
</section>
</template>
<script>
Polymer('custom-core-input', {
required: false
});
</script>
</polymer-element>
Note that the required attribute of custom-core-input must be initialized to false (or true, depending on your defaults).
The error message is only displayed if the input is invalid. So one option is to set the autovalidate attribute. Set required and inputError on your custom-core-input and you will see the error message on page load. More generally you want to set isInvalid to true or false depending on the current input validity.
Here is one way to do it, in the element prototype:
<script>
Polymer({
ready: function () {
//check the "custom-core-input" required attribute:
if (this.required)
//select the input element by Id, and set Required attr to True:
this.$.custominput.required = true;
}
});
</script>

Calling a polymer element within a polyment with JSON as parameter

I am calling a polymer element within another element. The inner polymer element has a published attribute to which I am binding JSON from the parent polymer. However it is not getting reflected.
<polymer-element name="parent-test" attributes="testData">
<template>
This is Parent test
<child-test testdatachild="{{testData}}"></child-test>
</template>
<script>
Polymer('parent-test', {
testData: [],
ready: function () {
debugger;
this.testData = [1, 2, 3, 4]
}
});
</script>
</polymer-element>
<polymer-element name="child-test" attributes="testDataChild">
<template>
<!--{{testDataChild}}-->
<template repeat="{{test in testDataChild}}">
{{test}}
</template>
</template>
<script>
Polymer('child-test', {
testDataChild: [],
ready: function () {
debugger;
}
});
</script>
</polymer-element>
I am not sure what could be the problem here.
Edit:
Seems like I am not having the actual parentContent at the time of generating the child-polymer-element.
If I assign hardcoded values in ready function for this.parentContent, it doesnt work as well.
If I assign hardcoded values in create function for this parent.Content, it works.
So, I am not not sure if this is something related to generating the child polymer element before the values getting binded to parent.
Thanks,
Sam
I modified your plunk example and get it working without your workaround :
Plunk
<polymer-element name="child-test" attributes="testdatachild">
<template>
<br><br>
In Child el.:
<br>
<template repeat="{{test in testdatachild}}">
{{test}}
<br>
</template>
</template>
<script>
Polymer('child-test', {
ready: function () {
}
});
</script>
This is Parent test
<child-test testdatachild="{{testData}}"></child-test>
<br>
</template>
<script>
Polymer('parent-test', {
created: function () {
this.testData = [1, 2, 3, 4];
}
});
</script>
The main problem seems to be the order of the code
I guess it works better to first declare the child, then the parent, as the child is used in the parent...
Also, as specified in the polymer documentation :
polymer
Important: For properties that are objects or arrays, you should always initialize the properties in the created callback. If you set the default value directly on the prototype (or on the publish object), you may run into unexpected “shared state” across different instances of the same element.
Here is modified example of you code that works : Plunk
Why your example is not working, I don't have all answers buy you are right for one:
<!-- This won't work cause:
"Attributes on child-test were data bound prior to Polymer upgrading the element.
This may result in incorrect binding types." -->
This is Parent test
<child-test testdatachild="{{testData}}"></child-test>

Is there a way to swap one polymer component for another?

I'm stuck a bit conceptually. I've created a builder component for managing the input of data via an AJAX post. On return, I'll have a JSON object that I can render to the client. Optimally, I'd like to instantiate a new render component, pass the JSON object to it, and then destroy the builder component (door number two is a simple page reload, but that seems like a very 1990s hammer for a 21st century nail).
Representative (simplified) builder component:
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/core-ajax/core-ajax.html">
<polymer-element name="post-builder" attributes="accesstoken">
<template>
<core-ajax id="poster" url="api_call" handleAs="json"></core-ajax>
<textarea class="form-control" rows="4" placeholder="Enter text here." value="{{ body }}"></textarea>
<div class="postControls">
<div class="sendLink">
Post
</div>
</div>
</template>
<script>
created: function(){
this.body = '';
},
ready: function(){
},
postAndReplaceTile: function(){
data = {body : this.body, publish : true};
var ajax = this.$.poster;
ajax.removeEventListener('core-response');
ajax.method = 'POST';
ajax.contentType = 'application/json';
ajax.params = { access_token: this.accesstoken };
ajax.body = JSON.stringify(data);
ajax.addEventListener('core-response', function(){
if(this.response.hasOwnProperty('post')){
if(this.response.post.hasOwnProperty('id')){
// valid JSON object of the new post
}
}
});
ajax.go();
}
});
</script>
</polymer-element>
At the stage of the valid JSON object, I'm looking for a moral equivalent of jQuery's replaceWith()...recognizing that the JSON object is in the component that's being replaced so I need to sequence these events carefully. Is there a way to cleanly reach up to the parent DOM and do these types of transformations?
You could use parentNode.host (see here) to access the container element and use DOM methods to replace the element but that's somehow an anti-pattern and breaks encapsulation (see here and here).
It's proably better to use events and let the container element take care of swaping the elements.