Add Eventlistener dynamically on vaadin combobox in Polymer Datatable - combobox

I try to listen on changes made in a vaadin combobox (value) inside an iron-data-table.
This is my complete Datatable:
<iron-data-table id="grid4" items="[[riskCombined]]" selection-enabled on-selected-items-changed="_selected" >
<data-table-column name="#" width="20px" flex="0">
<template>
<div class="index">[[item]]</div>
</template>
</data-table-column>
<data-table-column name="Risk" width="140px" flex="0">
<template>[[item.name]]</template>
</data-table-column>
<data-table-column name="State" width="100px" flex="0">
<template><vaadin-combo-box value=[[item.state]] items=[[item.settings]] on-value-change="valueChanged" id=combobox[[index]]> </vaadin-combo-box></template>
</data-table-column>
</iron-data-table>
I want to listen on value changes:
<data-table-column name="State" width="100px" flex="0">
<template>
<vaadin-combo-box value=[[item.state]] items=[[item.settings]] on-value- change="valueChanged" id=combobox[[index]]> </vaadin-combo-box>
</template>
</data-table-column>
I'm adding an eventlistener:
<script>
var combobox = document.querySelector('vaadin-combo-box');
combobox.addEventListener('value-changed', function(event) {
console.log(event.detail.value);
});
combobox.addEventListener('selected-item-changed', function(event) {
console.log(event.detail.value);
});
</script>
also tried with:
document.getElementById(...)
The problem is that the Eventlistener is just added to the first combobox. But since there are multiple boxes dynamically created, this doesn't work. How can I dynamically add the Eventlistener to multiple comboboxes? Or is there an other solution?
Edit: tried this:
valueChanged: function(event) {
console.log(event.detail.value);
}
Edit 2:
changed
on-value-change="valueChanged"
to
on-value-changed="valueChanged"
Now edit1 is working! But its firing already when the page loads the first time an sets the values of the combobox...

The correct way is to use the on-value-changed (notice the changeD) attribute inside the template. Using querySelector is unreliable since there might be new elements being added later on the the table is scrolled.
Because the elements are being recycled and reused, there's a new value being bound to the <vaadin-combo-box> elements when scrolling – hence firing value-changed events everytime a row is recycled.
Assuming you want the user to be able to change the item.state property, I suggest you instead bound that property two-way with value="{{item.state}}" and add a listener for the event item-changed.
See my example here: http://jsbin.com/heroqu/edit?html,console,output
The item-changed seems to be fired always twice, but it should still be OK to use.

Vaadin-combo-box already fires value-changed event when value changes. You can listen to value-changes event instead of creating your own event. Here's the documentation for vaadin-combo-box.
listeners:{'value-changed':'valueChanged'},
valueChanged:function(e){
//code goes here
}

Related

Iron-list not update after element property changed

I'm having problems re-rendering an iron-list after changing an item property.
I need iron-list to re-render to apply new classes just like it does when i push or splice items.
Already tried almost everything(notifyPath, resize, _update, ...) but still doesn't make it work.
Please help :)
Below you can find an jsfiddle to ilustrate:
work OK:
this.set('words.'+i+'.checked', true);
NOT work:
this.notifyPath('words.'+i+'.checked');
http://jsfiddle.net/s6f029j3/23/
I looked at your jsfiddle and made some changes. Try it now and see if this is what you were looking to do.
jsfiddle
I made changes to the function on the paper-list to have it see if it had been checked there, rather than in the actual function.
<iron-list id="list" items="{{words}}">
<template>
<paper-item class$='[[_computedClass(item.checked)]]'>
<div>Item: [[item.name]] checked: [[item.checked]]</div>
</paper-item>
</template>
</iron-list>
and in the function, here's what I did:
_computedClass: function(e) {
//WHY IT DOES NOT RE-RENDER AFTER CLICK????
return (e) ? 'stuff_checked' : 'stuff_notchecked';
},
By just removing the .checked it now works as intended.

Using contenteditable with ng-model inside ng-repeat?

Here is my issue:
I am using ng-repeat to make a list of spans.
Each span has the contenteditable attribute and ng-model directive.
Everything works as expected (including two-way data binding), until I try to add a new item to the list.
<div ng-repeat="item in list">
<span ng-model="item.text" contenteditable></span>
</div>
<button ng-click="addItemToList"></button>
The methods look like this:
$scope.addItemToList = function () {
$scope.list.push({text: 'dummy text'});
}
or
$scope.addItemToList = function () {
$scope.list.splice(1, 0, {text: 'dummy text'});
}
When adding the new item to the list (push or splice), the DOM updates, but the last item is initialised empty, with no text whatsoever. The last item in the model list also empties out, even if I specifically push an element with text in it.
After a few tests, I've noticed that this only happens if the list's length is bigger after modifying it:
if I try to replace/modify/remove (not add) an element in the list, it works well.
I believe this has to do with the way contenteditable elements initialise in the DOM (I think they initialise empty, and somehow, the model empties out as well).
Has anyone encountered this problem before? If yes, how did you solve it / what workaround have you found?
Based on the angular docs related to ngModelController, it seems that there is not built-in support for two-way data binding with contenteditable elements, seeing as in the plunkr example they wrote their own contenteditable directive. You might be able to use a modified version of that as a workaround.
It looks to be a similar problem as this question and the contenteditable directive there looks similar to the contenteditable directive in the angular docs example.
I also found this directive on github that might accomplish what you are trying to do.
Edit: I did a new version of the plunk I posted in the comment above:
https://plnkr.co/edit/v3elswolP9AgWHDIPwCk
In this version I added a contenteditable directive that appears to be working correctly. It is basically a spin off of how the input[type=text] directive is written in angular, but I took out the places where it handles different types of input (since in this case it will just be text) and the places where it handles events that contenteditable elements don't even fire. I also changed it so that the $viewValue gets updated based on element.html() instead of element.val(). You might be able to use something like this as a workaround
The issue is old but that was the same problem for me today. (angular 1.5). My workaround was to add on blur update option: <td contenteditable data-ng-model="position.value" ng-model-options="{updateOn: 'blur'}"></td> somehow then model stopped getting be empty on initialize. I like using update on blur in many places (solves some performaces issues) so this is good for me, however it's some kind of trick.

How to respond to onChange event for a dropdown in angularify

I'm trying to use angularify in a project, but I'm facing the given problem:
I'm not able to get my onChange function called when a dropdown value changes. I have a second dropdown box which the listed values depends upon the first one.
How can I achieve the intended behavior with angularify?
I was looking, debugging and trying, and looks like I will not be able to do this because de dropdown directive writes a bunch of div's to my html. There is no select elmeent in any place of the document. How will a div element fire a onChange event?
Here is my markup:
Parent select
<dropdown title="Selecione" class="col-6" ng-model="novoProcedimento.procedimento" ng-change="buscarLocaisExecucao(novoProcedimento.procedimento)">
<dropdown-group class="meudropdown" title="proc.nome" value="proc.nome" ng-repeat="proc in procedimentosPermitidos">{{proc.nome}}</dropdown-group>
</dropdown>
Child select - Depends on parent's selection
<dropdown title="Selecione" id="dente-regiao" class="col-5" ng-model="novoProcedimento.local" ng-change="buscarFaces(novoProcedimento.procedimento, novoProcedimento.local)">
<dropdown-group title="local" value="local" ng-repeat="local in locaisExecucao">{{local.descricao}}</dropdown-group>
</dropdown>
Controller - Intendend to be fired on parent's change.
$scope.buscarLocaisExecucao = function (procedimento){
selecaoProcedimentoService.buscarLocaisExecucao(procedimento)
.then(function(locaisExecucao) {
$scope.locaisExecucao = locaisExecucao;
});
}
Am I using the wrong framework? Am I missing something?
Thank you.

How do I dynamically style uib-accordion-group

I have created a uib-accordion in my Angular website and can get most of the functionality I want, with dynamic content changing accordingly.
I am having trouble styling the uib-accordion-group dynamically.
<uib-accordion-group panel-class="panel-danger">
<uib-accordion-heading>
Accordion Heading 1
Is fine and colours the whole heading Red/Pink, I want to change this to panel-warning or panel-info based on other variables on the page.
<uib-accordion-group panel-class="{{getPanelColor()}}">
<uib-accordion-heading>
Accordion Heading 1
The function seems to be called correctly and is triggered correctly with ng-click elsewhere.
I appears that I cannot change the value panel-class uses dynamically. So in this instance getPanelColor() returns 'panel-danger', 'panel-info' or 'panel-warning' depending on other variables. If I print this return value out on the page in another div or whatever it changes correctly. If I refresh the page the correct colours are displayed for the changed panel-group.
Is there another way of setting the color - I don't know what the classes are for the accordion-group. I have tried changing the color of a div withing the panel, but this is a child element and does not change the color of the whole heading.
Any help much appreciated. (I'll come up with a JSFiddle if the question is not clear)
If you look at the HTML after the panel-class has changed and Angular has digested the change, you will see this line:
<div class="panel panel-danger" ... panel-class="panel-default">
That is, there is a mismatch between class and panel-class (the former has panel-danger, whereas the latter has panel-default). The uib-accordion-group directive simply does not handle the change in the wanted manner.
One workaround is to add ng-if to the whole group:
<uib-accordion-group ng-if="render" panel-class="{{getPanelColor()}}">
... and just before you want to change panel-class, remove the whole element temporarily, so that Angular re-renders it from scratch. Hopefully, the following code explains the principle:
$scope.render = true;
$scope.panelColor = 'panel-danger';
$scope.setPanelColor = function(val) {
$scope.panelColor = val;
$scope.render = false;
$timeout(function () {
$scope.render = true;
});
};
$scope.getPanelColor = function() {
return $scope.panelColor;
};
See the proposal in action: http://plnkr.co/edit/XfJiPnNi1z4F9cgIVxxw?p=preview. Press 'Clear panel color OK'.
The downside is that the removal of the element causes some flickering.
I have added another button 'Clear panel color FAIL' that shows what happens in your failing case. Here is what the HTML looks like after you press the button, notice the mismatch panel-danger vs. panel-default:
Use an interpolated expression in the class attribute, for example:
class="{{!ctrl.valid?'notValid':'valid'}}"

AngularJS inconsistent databinding

I'm learning AngularJS and I have a question regarding the databinding for select elements. The databinding for textboxes works without any kind of event handling code. Once the ng-model attribute is set textbox updates when the model property changes and vice versa. There is no need for ng-change attribute.
However, for select elements we need to write functions that will be called via ng-change atribute.
Why does angularjs handle databinding without an ng-change attribute for textboxes but requires functions that will be called via ng-change attribute for select elements?
UPDATE:
Added the fiddle in the comments section. The example is from AngularJS in Action book. Click on one of the stories, change the textbox value and the model is updated. Change the selection in dropdown model is not updated.
UPDATE:
Added a new fiddle in the comments.
Thanks.
I've created a fiddle that works here - The issue is really just the dummy data here. In the original fiddle, the object created in the statuses array for {name:'Back Log'} and {name:'To Do'} are not the same (not ===) as the {name:'Back Log'} and {name:'To Do'} objects created in the dummy story objects.
To make the example work, I pass the indexed statuses into the getStories function. However I think this is really just a case of demo-induced confusion. (I've been looking at the MEAP for Angular in Action as well, and I think it could be simplified a bit like this one, that uses simple string statuses that will pass the === test
var getStories = function(statusesIndex) {
var tempArray = [
{title:'Story 00',
description:'Description pending.',
status: statusesIndex['To Do']
},
{title:'Story 01',
description:'Description pending.',
status: statusesIndex['Back Log']
}
];
return tempArray;
}
I think your confusion might be a result of the select documentation still being incorrect. (See my Disqus comment.) ng-model can and should be used with select. ng-change is optional and it just gives you a hook should you want to do something each time the selected option changes.
Normally you should use ng-options with select.
If i understood your question correctly then I think your guessing is wrong because for select boxes, you do not have to invoke ng-change event in order to fetch the selected option.
<select ng-model='select'>
<option>....</option>
<option value='one'>One</option>
<option value='Two'>Two</option>
</select>
// Your selected option will print below... without invoking ng-change
<div>You selected: {{select}}</div>
Demo: http://jsfiddle.net/jenxu/1/

Resources