I have a system where I can select a value in a vaadin-combobox or select the value in another div (with a svg) and set the combo's value dynamically. How can I set the combo's value?
I already tried value="", but this didn't work...
I simply set:
item-label-path="nombreCorto" item-value-path="idWaEmpresa" value="1"
UPDATE To bind the combobox's value to the first array item, you'd use a computed binding:
<vaadin-combo-box
value="[[_getFirstItem(sessions)]]"
...>
</vaadin-combo-box>
where _getFirstItem() is a method on your Polymer object:
Polymer({
is: 'x-foo',
...
_getFirstItem: function(sessions) {
return sessions.length > 0 && sessions[0];
}
});
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="https://cdn.vaadin.com/vaadin-core-elements/master/vaadin-combo-box/vaadin-combo-box.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<vaadin-combo-box
label="Element"
items='[[sessions]]'
value="[[_getFirstItem(sessions)]]">
</vaadin-combo-box>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-foo',
properties : {
sessions: {
type: Array,
value: function() {
return ["Bohrium", "Boron", "Bromine", "Cadmium", "Caesium", "Calcium"];
}
}
},
_getFirstItem: function(sessions) {
return sessions.length > 0 && sessions[0];
}
});
});
</script>
</dom-module>
</body>
codepen
From docs for vaadin-combobox:
You need to provide the set of items which the user can select with the items property. Current selection is indicated by the value and selectedItem properties. You can set or change the selection programmatically by setting the value property. Doing so also updates the visible fields.
When setting the items declaratively, notice that the attribute value needs to be a valid JSON string. You need to use single quotes for the attribute value and double quotes inside the value (in the JSON string). Alternatively, you can escape the double quotes inside the value.
<vaadin-combo-box
label="Element"
items='["Bohrium", "Boron", "Bromine", "Cadmium", "Caesium", "Calcium"]'
value="Bromine">
</vaadin-combo-box>
Setting the items and value programmatically:
var combobox = document.querySelector('vaadin-combo-box');
combobox.items = ['Bohrium', 'Boron', 'Bromine', 'Cadmium', 'Caesium', 'Calcium'];
combobox.value = 'Bromine';
Related
I have a select with ngOptions based on an array. This array can change.
If the new array value does not contain the selected option value, the option value is set to undefined by the selectController. Is there a way to prevent this ?
Plunker : https://plnkr.co/edit/kao3h5ivHXlP1Wrdx1Ib?p=preview
Scenario:
select Blue/Red or Green color
click on Reduced to only have Black and White options
See that the model value is left to blank
Wanted behavior : that the model value stays at Blue/Red or Green
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
</head>
<body ng-app="selectExample">
<script>
angular.module('selectExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.colorsFull = [
{id:"bk", name:'black'},
{id:"w", name:'white'},
{id:"r", name:'red'},
{id:"be", name:'blue'},
{id:"y", name:'yellow'}
];
$scope.colors = $scope.colorsFull;
$scope.selectedColor =$scope.colorsFull[0];
$scope.colorsReduced = [
{id:"bk", name:'black2'},
{id:"w", name:'white2'}];
}]);
</script>
<div ng-controller="ExampleController">
<button ng-click="colors=colorsReduced">Reduced</button>
<button ng-click="colors=colorsFull">Full</button>
<br/>
Colors : {{colors}}
<hr/>
<select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id">
</select>
selectedColor:{{selectedColor}}
</div>
</body>
</html>
You can achieve this by keeping track of what color is selected in the full colors dropdown, and inserting it into the reduced colors array. First, add an ng-change directive so that we can keep track of the selected color:
<select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id" ng-change="setColor(selectedColor)">
And in your controller:
$scope.setColor = function(color) {
if(color !== null) {
// Keep track of the color that is selected
$scope.previousColor = color;
}
else {
// Changed arrays, keep selected color in model
$scope.selectedColor = $scope.previousColor;
}
}
Now ng-model is set to the correct color whenever the arrays are changed, but it will appear blank in the reduced colors dropdown because the option doesn't exist. So, we need to insert that option into the array. However, switching back and forth between dropdowns will cause the reduced colors array to keep on adding more options, and we only want to remember the option we selected from the full colors array. So, we need create an initial set of colors to revert back to when switching.
// Keep a copy of the original set of reduced colors
$scope.colorsReducedInitial = [
{id:"bk", name:'black2'},
{id:"w", name:'white2'}];
Finally, we need to insert the selected option into the reduced colors array. Change the ng-click on the Reduced button to use a function:
<button ng-click="setColorsReduced()">Reduced</button>
Now, we can insert the option, after resetting the reduced colors array to its initial state:
$scope.setColorsReduced = function() {
// Revert back to the initial set of reduced colors
$scope.colors = angular.copy($scope.colorsReducedInitial);
if($scope.previousColor !== undefined) {
var found = false;
angular.forEach($scope.colorsReducedInitial, function(value, key) {
if(value.id == $scope.previousColor.id) {
found = true;
}
});
// If the id is found, no need to push the previousColor
if(!found) {
$scope.colors.push($scope.previousColor);
}
}
}
Note that we are looping through the reduced colors array to ensure we aren't duplicating any colors, such as black or white.
Now, the reduced colors ng-model has the previous dropdown's selected color.
Updated Plunkr Demo
Use below code in script
$scope.makeSelected=function(){
$scope.selectedColor =$scope.colorsReduced[0];
}
And just add this function call in reduced button line like below
<button ng-click="colors=colorsReduced;makeSelected()">Reduced</button>
This will do what you want to achieve.
Using Jukebox's answer, I ended-up writing a directive, using the modelCtrl.$formatters to get the initial value. It also offer the possibility to store the previousValue in the scope or in a local variable :
Usage: <select .... select-keep> or <select .... select-keep="previousColor">
Directive:
.directive('selectKeep', function($parse) {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {
var previousValueGetter;
var previousValueSetter;
if (attrs.selectKeep) { //use a scope attribute to store the previousValue
previousValueGetter = $parse(attrs.selectKeep);
previousValueSetter = previousValueGetter.assign;
}
else { //use a local variable to store the previousValue
var previousValue;
previousValueGetter = function(s) { return previousValue;};
previousValueSetter = function(s, v) { previousValue = v;};
}
//store the initial value
modelCtrl.$formatters.push(function(v) {
previousValueSetter(scope, v);
return v;
});
//get notified of model changes (copied from Jukebox's answer)
modelCtrl.$viewChangeListeners.push(function() {
if (modelCtrl.$modelValue !== null) {
previousValueSetter(scope, modelCtrl.$modelValue);
} else {
modelCtrl.$setViewValue(previousValueGetter(scope));
}
});
}
};
Plunker
Edit : it has a flaw, the form gets dirty even if the value does not change. I had to add these lines in the else of the viewChangeListener but it doesn't look nice. Any ideas ?:
...
} else {
modelCtrl.$setViewValue(previousValueGetter(scope));
//set pristine since this change is not a real change
modelCtrl.$setPristine(true);
//check if any other modelCtrl is dirty. If not, we will have to put the form as pristine too
var oneDirty =_.findKey(modelCtrl.$$parentForm, function(otherModelCtrl) {
return otherModelCtrl && otherModelCtrl.hasOwnProperty('$modelValue') && otherModelCtrl !== modelCtrl && otherModelCtrl.$dirty;
});
if (!oneDirty) {
modelCtrl.$$parentForm.$setPristine(true);
}
}
I have a method to save an object. That object is added to an array after its saved. The object has many properties . So, before adding the object to an array I am modifying few properties. Few of them don't reflect in UI .
Code :
HomeController.js
$scope.MainArray=[];
$scope.newItem={};
AdjustmentController.js
$scope.Save = function(item){
$scope.newItem={};
var promiseObj= $http.post('My_Url',{expectedItem: item});
promiseObj.success(function(data,status){
$scope.newItem.Id= data;
$scope.newItem.dataList= item.dataList;
$scope.newItem.LatestComment = item.LatestComment;
$scope.newItem.CreatedDate = item.CreatedDate;
if($scope.MainArray.length==0){
$scope.MainArray.push($scope.newItem);
}
else{
$scope.MainArray.unshift($scope.newItem);
}
})
}
HTML :
<body ng-controller="HomeController">
<div ng-controller="AdjustmentController">
<div ng-repeat="item in MainArray ">
<!-- This past is not updated -->
<span>{{item.LatestComment}}</span>
<span>{{item.CreatedDate}}</span>
<!-- This past is updated -->
<span>{{item.DataList[0].text}}</span>
<span>{{item.DataList[1].text}}</span>
<span>{{item.DataList[2].text}}</span>
<span>{{item.DataList[3].text}}</span>
</div>
</div>
</body>
The value is changes if I console and see. But in UI it updated only few values and LatestComment and CreatedDate is not updated.
I have also tried using $scope.$apply() , but it did not work.
You need to initialize variable $scope.MainArray when controller loads, then after you need to just over write it when it will needed to save.
In your controller define variable like this :
$scope.MainArray=[];
and then use it in your save object function.
Here, you only send the item to your backend:
var promiseObj= $http.post('My_Url',{expectedItem: item});
and here you expecting it to be changed:
$scope.newItem.dataList= item.dataList
$scope.newItem.LatestComment = item.LatestComment;
$scope.newItem.CreatedDate = item.CreatedDate;
Your answer is contained in the data object, not the item. item won't ever change this way.
Had created and used my custom polymer element which is a table. Now, I want to use the check box element from their catalog in my table.
However, I keep getting this error when I reference the check box html file in my index page:
DuplicateDefinitionError: a type with name 'dom-module' is already
registered
This is how I have created my custom element:
<!-- Imports polymer -->
<link rel="import" href="polymer/polymer.html">
<script src="underscore-min.js"></script>
<!-- Defines element markup -->
<dom-module id="custom-table" >
<template>
<style>
ul {list-style-type:none; display:block}
ul li {display:inline; float:left; padding:20px; width:1.5em; border-bottom:1px solid #eee}
</style>
<h2>{{title}}</h2>
<table id="dataTable">
<thead id="tableHead"></thead>
<tbody id="tableBody"></tbody>
</table>
</template>
</dom-module>
<!-- Registers custom element -->
<script>
Polymer({
is: 'custom-table',
// Fires when an instance of the element is created
created: function() {
},
// Fires when the local DOM has been fully prepared
ready: function() {
var context= this;
this.pageNo=0;
this.totalPages=0;
// set the default paging size:
if(this.page== null|| this.page==undefined)
this.page=10;
// delegate the change selection handler to the table body
this.$.tableBody.addEventListener("click",function(e){
if(e.target && e.target.nodeName == "INPUT") ;
{
context.changeSelection(e.target);
}
});
},
// Fires when the element was inserted into the document
attached: function() {},
// Fires when the element was removed from the document
detached: function() {},
// Fires when an attribute was added, removed, or updated
attributeChanged: function(name, type) {
alert("changed");
},
loadData: function(columns,data){
this.data = data;
// add the selected property to the values
for(var i=0;i<this.data.length; i++) { this.data[i].Selected = false;}
this.filteredData=this.data;
this.columns = columns;
//initialize the filteredData
this.filteredData=data;
// calculate the total number of pages
this.totalPages= Math.ceil(data.length/this.page);
this.drawTableHeader();
_.defer(this.applyFilters,this);
_.defer(this.drawTableBody,this);
},
drawTableHeader:function(){
var columns = this.columns;
// load the header
var headTr = document.createElement('tr');
//add a blank header for the check box;
var th=document.createElement('th');
headTr.appendChild(th);
for(var i = 0; i<columns.length ;i++)
{
var td=document.createElement('th');
// if the column is sortable then add the event listener for sorting it
if(columns[i].Sortable)
{
td.addEventListener("click",function(){ this.sortBy(columns[i].Title); });
}
td.innerText = columns[i].Title;
headTr.appendChild(td);
}
this.$.tableHead.appendChild(headTr);
},
drawTableBody: function(context){
// this is a defered function
var context = context;
// get the number of items according to the current page number
var pageItems= context.filteredData.slice((context.page*context.pageNo),((context.page*context.pageNo)+context.page));
console.log(pageItems);
// print the page items
for(var i=0; i < pageItems.length; i++)
{
var currItem = pageItems[i];
var tr= document.createElement("tr");
// add the check box first
var checkbox= document.createElement("input");
checkbox.type="checkbox";
checkbox.checked=pageItems[i].Selected;
var ItemId = currItem.Id;
checkbox.setAttribute("data-ItemId",ItemId-1);
var td=document.createElement('td');
td.appendChild(checkbox);
tr.appendChild(td);
// for every column specified add a column to it
for(var j = 0; j< context.columns.length; j++)
{
var td=document.createElement("td");
td.innerText= pageItems[i][context.columns[j].Title];
tr.appendChild(td);
}
//append the row to the table;
context.$.tableBody.appendChild(tr);
} // end for i
},
applyFilters:function(context){
if(context.filter)
{
alert("filterApplied");
}
},
changeSelection:function(checkbox){
var ItemId = checkbox.getAttribute("data-ItemId");
this.data[ItemId].Selected= checkbox.checked;
console.log(this.data[ItemId]);
},
properties:{
title :String,
columns:Array,
data:Array,
page:Number,
filters:Object,
Selectable:Boolean
}
});
</script>
and here is what my index page looks like:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><my-repo></title>
<!-- Imports polyfill -->
<script src="webcomponents-lite.min.js"></script>
<!-- Imports custom element -->
<link rel="import" href="my-element.html">
<link rel="import" href="bower_components/paper-checkbox/paper-checkbox.html">
</head>
<body unresolved>
<!-- Runs custom element -->
<custom-table title="This is data table"></custom-table>
<script>
document.addEventListener("WebComponentsReady",function(){
var data = [{'Id':1,'firstName':'aman',age:25},{'Id':2,'firstName':'gupta',age:25}];
var cols = [{Title:'firstName',Sortable:true},{Title:'age',Sortable:false}];
var a = document.querySelector('my-element');
a.loadData(cols,data);
});
</script>
</body>
</html>
I've just started out with polymer and I'm not quite sure what's going on here..
Thank you in advance :)
I got what the problem is..
My custom element was referencing a different Polymer.html file.
Silly me :D
I'm using Polymer Starter Kit Yeoman generator on Windows and I had the same problem:
Error: DuplicateDefinitionError: a type with name 'dom-module' is already registered
This error is triggered in Firefox console. Chrome works fine.
The components created with the generator (example: yo polymer:el my-element) have this polymer.html import:
<link rel="import" href="..\..\bower_components/polymer/polymer.html">
The base path is described with "backslash".
In some custom polymer elements I created by myself, I imported polymer.html with:
<link rel="import" href="../../bower_components/polymer/polymer.html">
And I think this lead to a duplication of some kind. To solve the problem, I just changed all automatically created imports, using only forward slashes /.
Hope this helps someone.
If I have 2 divs (removed ng-click function for simplicity)
<div ng-class="{selected: header.type == 'percent'}" data-type="percent"></div>
<div ng-class="{selected: header.type == 'invisible'}" data-type="invisible"></div>
This will apply the class of .selected to one of the divs, depending on the value of $scope.header.type
However, I also have it so when I click on the div that does not have the .selected class, i remove the selected class from the div that had it, and apply it to the div that was just clicked.
Now, on the controller, how do I get the data-type of the div that has the .selected class?
Basically I'm trying to set $scope.header.type to hold the value of data-type of the div that has the .selected class
Just in case it's needed, here's the ng-click fn (which is not angular-like, but I couldn't find an alternative)
$scope.changeOfferbox = function($event) {
var selected = angular.element(document.querySelector('.selected'))
selected.removeClass('selected')
var clicked = angular.element($event.target).addClass('selected')
}
A simple solution might be to pass the data-type to your click function ; this way, you actually don't even need to manually add/remove classes, the ng-class directives will automatically update when header.type changes :
$scope.changeOfferbox = function($event, localType) {
if (localType !== $scope.header.type) {
$scope.header.type = localType;
}
};
<div ng-class="{selected: header.type == 'invisible'}" ng-click="changeOfferbox('invisible');" data-type="invisible"></div>
I need to output the values and perform some calculations with the values of AngularUI sliders.
I have this mark-up:
<body ng-controller="sliderDemoCtrl">
<div ui-slider="{range: 'min'}" min="0" max="50" ng-model="demoVals.slider"></div>
<p>The value so far is:
<span ng-bind="demoVals.slider"></span> // I want this to show 0 until the slider is moved
</p>
<p>The calculated value is: <input type="text" ng-model="calculated" value=" {{ calculated }}" placeholder=0 /></p>
</body>
Firstly, I can't work out how to initialise that <span ng-bind="demoVals.slider"></span> with a default value of 0 or "None". At the moment it is blank until the slider moves. How can I set this to a default value?
Secondly, I want the value of {{ calculated }} to be a number multiplied by the value of the slider. How can I pass the value of the slider to ng-model="calculated" or access the value from within my controller?
Here's my Plunkr
You can set the initial value of the ng-model in the $scope:
$scope.demoVals = {};
$scope.demoVals.slider = 0;
And to keep the value of calculated in one way sync with the demoVals.slider, you can use a $watch on the $scope:
$scope.$watch('demoVals.slider', function (newVal) {
if (typeof newVal !== 'undefined') {
$scope.calculated = newVal * 3.14159; // Use any value here.
}
});
Working demo.
The way ng-model works, it binds to the scope property you define in it's attribute value in the markup. If no such scope property exists, it gets created on initialization.
Thus, to set start value just create that model property. To adjust another scope property based on changes to the slider model you can use $watch to listen for changes to the slider model property
app.controller('sliderDemoCtrl', function($scope) {
/* ng-model bound to this object, so define it's start value*/
$scope.demoVals={slider:10}
/* watch for changes*/
$scope.$watch('demoVals.slider',function(newVal,oldVal){
if(newVal){
$scope.calculated=2*newVal;
}
})
$scope.calculated = 0;
});
DEMO