How addEventListener in nested template? - polymer

In the following code:
<body unresolved fullbleed layout vertical>
<template is="auto-binding" id="app">
<core-ajax auto url="http://testesapi.azurewebsites.net/api/padaria" params='{"alt":"json", "q":"chrome"}' handleAs="json" response="{{produtos}}" id="ajax"></core-ajax>
<template id="template2" repeat="{{prod in produtos}}">
<form class="myForm" is="ajax-form" action="http://testesapi.azurewebsites.net/api/padaria" method="put">
<input type="hidden" name="id" is="core-input" value="{{prod.id}}">
<paper-input name="nome" label="Nome" value="{{prod.nome}}" floatinglabel></paper-input>
<paper-input name="Preco" label="Preço" value="{{prod.Preco}}" floatinglabel>></paper-input>
<button type="submit">SALVAR</button>
</form>
</template>
</template>
</body>
How can I add an Event Listener to ".myForm"? I need to call core-ajax go() method after ajax-form is 'submitted'.
My problem is that querySelectorAll('.myForm') is resulting null, so it appears it's not in the DOM yet.
I tried to put querySelector inside this (but no success):
app.addEventListener('template-bound', function() {});
It's a Polymer app.
Update 1
I got take the nested template giving it an id of "template2" and:
var app = document.querySelector('#app');
app.addEventListener('template-bound', function()
var template2 = app.$.template2;
<== question now is: how to "for each" the forms inside template2 to add an Event Listener?
});

Have you tried just adding declarative event handlers (on-event attributes)? ajax-form has submitting and submitted events, so presumably you could do something like:
<form class="myForm" is="ajax-form" on-submitting="myHandler" action="http://testesapi.azurewebsites.net/api/padaria" method="put">

With querySelectorAll('.myForm')[0] this specifies the 1st element of that kind.
Currently for perfrormance (tiny gain) you can use getElementById() if you have just 1 form.
Edit:
Here's a rough example of using querySelector & querySelectorAll.
var form = document.querySelectorAll('.myForm')[0].style.backgroundColor = 'red';
var form = document.querySelector('.myForm2').style.backgroundColor = 'green';
For created elements querySelectorAll is useless as it only returns a static list.You can create an element and get the element via the example using ID as it returns the actual node list.
var section = document.getElementById('section');
section.children[3];
https://jsfiddle.net/kroq55td/1/

Related

Data binding between published properties of two custom elements inside an auto binding template - Polymer 1.0

Problem: I have an auto binding template in my main index.html page. Inside the template I am using two of my custom elements. One element is the producer of some data and the other one is the consumer of that data. These custom elements expose published/declared properties for each other to use and bind to. I was able to do that in Polymer 0.5 fairly easily (an example shown below). How do I do the same in Polymer 1.0?
How I used to do in Polymer 0.5?
In Polymer 0.5 I used to data bind between published properties of two custom elements using curly brace syntax and then inside it used the auto node finding concept to directly bind to other element's published property. An example shown below,
<template is="auto-binding">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
I tried something similar in Polymer 1.0 as shown in the example below
<template is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
But it throws an error:-
Uncaught TypeError: Cannot read property '$' of undefined
You can't do $.* bindings inside the template in Polymer 1.0. Instead, either refactor or use computed functions.
In your situation, since selectedLabel and toolbartitle shares the same value, it is much better to simply bind them to the same property.
Also, attribute names that are declaratively passed in (through the element tag) need to be serialized, so selectedLabel becomes selected-label.
<body>
...
<template id="tpl" is="dom-bind">
<my-navigation selected-label="{{myLabel}}" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{myLabel}}" id="my_scaffold"></my-scaffold>
</template>
<script>
...
window.addEventListener("WebComponentsReady", function (e) {
document.querySelector("#tpl").myLabel = "Home";
...
});
...
</script>
</body>
There is probably a better way to do that, but you can try this:
<body>
<template id="app" is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
<script>
var app = document.querySelector('#app');
app.addEventListener('template-bound', function () {
console.log('Our app is ready to rock!');
});
window.addEventListener('WebComponentsReady', function () {
document.querySelector('body').removeAttribute('unresolved');
var my-navigation = document.querySelector('my-navigation');
// This will add the variable to the 'app' context (template)
app.selectedLabel = my-navigation.selectedLabel;
});
</script>
</body>

Polymer paper-input-decorator updateLabelVisibility() is not working

I don't know how I am supose to use the updateLabelVisiblity() function on a paper-input-decorator element. This should work but it doesn't!
In my plunker a normal inputs value is connected to a paper-inputs value. Whenever I type something in my normal input a function is called. That function calls validate() and updateLabelVisiblity.
Here is a plunker and here is my Polymer-element.
<polymer-element name='my-input'>
<template>
<h3>Paper input (linked)</h3>
<paper-input-decorator id='myPaperInput' label='Field' error='error' floatingLabel autovalidate>
<input is='core-input' pattern='^[0-9]*$' value='{{something}}'>
</paper-input-decorator>
<h3>Normal input (linked)</h3>
<input value='{{something}}' on-keyup='{{mykeyup}}'>
<p>
Why doesn't the label show above the paper-input when i start typing in the <b>Normal Input</b>?
</p>
</template>
<script>
Polymer({
mykeyup: function(){
this.$.myPaperInput.validate();
this.$.myPaperInput.updateLabelVisibility();
}
});
</script>
</polymer-element>
Solved
mykeyup: function(){
var p = this.$.myPaperInput;
p.validate();
p._autoLabelVisible = p.querySelector('input').value !== '' ? false : true;
}
I see what's happening. The updateLabelVisibility requires the value in order to show the floating label. However instead using updateLabelVisibility you can also do this hack:
this.$.myPaperInput._autoLabelVisible = false;
Which basically just hard sets the value that updateLabelVisibility is setting.
Here is your code, just updated:
http://plnkr.co/edit/l4xQBv7PKft2zqARnPu1?p=preview
Reference:
https://github.com/Polymer/paper-input/blob/master/paper-input-decorator.html#L474

Remove child element's attribute from Polymer js

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 :)

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>

html5 form checkValidity() method not found

I am trying to use the form method checkValidity().
http://html5test.com/ tells me that my browser (Chrome) support the form-level checkValidity method.
However, using jsfiddle http://jsfiddle.net/LcgnQ/2/ I have tried the following html and javascript snippets:
<form id="profileform" name="profileform">
<input type="text" id="firstname" required>
<input type="button" id="testbutton" value="Test">
</form>
$('#testbutton').bind('click',function(){
try{
alert($('#profileform').checkValidity());
}
catch(err){alert('err='+err)};
});
I'm getting an error: object has no method checkValidity()
What am I doing wrong?
Try:
$('#profileform')[0].checkValidity()
When you select $('#profileform') you get a jQuery object array. To access actual DOM properties you must select the first item in the array, which is the raw DOM element.
#robertc 's Answer is perfect. Anyway I'd just add another way to do it using jQuery's .get([index]) function. It also retrieves the DOM element for the given index, or all of the matched DOM elements if there's no index declared.
In the end it is exactly the same, only written in a bit more verbose way:
$('#profileform').get(0).checkValidity()
Leaving you the docs right here: https://api.jquery.com/get/
Just another way:
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByName('profileform');
// Loop over them and prevent submission
var validation = Array.prototype.filter.call(forms, function (form) {
form.addEventListener('submit', function (event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
}, false);
});