Polymer 1.0 Problems with dom-repeat - polymer-1.0

I hope someone could help me. I'm really frustrated. :-( I can't figure out how to use the new dom-repeat template with polymer 1.0.
I want to show same items from firebase in a custom-element-list, but if I load the items from firebase, my custom-element-list doesn't fill with the items.
Please see the Code. BIG THANKS in the meantime.
Custom Element: my-uebersicht
<dom-module id="my-uebersicht">
<style>
:host {
display: block;
}
#fabTest {
position: absolute !important;
right: 10px;
top: 10px;
}
</style>
<template>
<h1 class="paper-font-display1"><span>Übersicht</span></h1>
<my-zeiteintrag-list zeiteintraege="{{zeiteintraege}}"></my-zeiteintrag-list>
<paper-fab id="fabTest" mini icon="polymer" on-click="loadUebersicht"></paper-fab>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'my-uebersicht',
routeTo: function(route) {
document.querySelector('#app').route = route;
},
loadUebersicht: function() {
var id = document.querySelector('#app').getUserId();
var uname = document.querySelector('#app').getUsername();
if ((typeof id === 'undefined') || (typeof uname === 'undefined')) {
this.routeTo('login');
}
var that = this;
var rootRef = new Firebase("https://<FIREBASE.com>/" + id);
rootRef.on("value", function(snapshot) {
snapshot.forEach(function(child) {
var zeintrag = child.val();
that.zeiteintraege.push(zeintrag);
});
});
},
ready: function() {
this.zeiteintraege = [];
}
})
})();
</script>
Custom Element: my-zeiteintrag-list
<dom-module id="my-zeiteintrag-list">
<style>
:host {
display: block;
}
</style>
<template>
<template is="dom-repeat" items="{{zeiteintraege}}">
<my-zeiteintrag-item zeiteintrag="{{item}}"></my-zeiteintrag-item>
</template>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'my-zeiteintrag-list',
properties: {
zeiteintraege: {
type: Array,
value: [],
notify: true,
reflectToAttribute: true
}
},
ready: function() {
this.zeiteintraege = this.zeiteintraege || [];
}
});
})();
</script>
Custom Element: my-zeiteintrag-item
<dom-module id="my-zeiteintrag-item">
<style>
:host {
display: block;
}
</style>
<template>
<paper-material elevation="1">
<ul>
<li>Projekt: <span class="paper-font-body1">{{zeiteintrag.projekt}}</span></li>
<li>Vorgang: <span class="paper-font-body1">{{zeiteintrag.vorgang}}</span></li>
<li>Artikel: <span class="paper-font-body1">{{zeiteintrag.artikel}}</span></li>
<li>Datum: <span class="paper-font-body1">{{zeiteintrag.datum}}</span></li>
<li>Dauer: <span class="paper-font-body1">{{zeiteintrag.dauer}}</span></li>
<li>Bemerkung: <span class="paper-font-body1">{{zeiteintrag.bemerkung}}</span></li>
</ul>
</paper-material>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'my-zeiteintrag-item',
properties: {
zeiteintrag: {
type: Object,
value: {},
notify: true,
reflectToAttribute: true
}
},
ready: function() {
this.zeiteintrag = this.zeiteintrag || {};
}
});
})();
</script>
[EDIT] - found a solution
After pointing to a Polymer Github Issue about dom-repeat at the Polymer Slack Chat Github Issue and read the Documentation again. You must use the Polymer methods (push, pop, splice, shift, unshift) for Arrays to trigger an update.
Here is the working solution:
Custom Element: my-uebersicht
<script>
(function() {
Polymer({
is: 'my-uebersicht',
routeTo: function(route) {
document.querySelector('#app').route = route;
},
loadUebersicht: function() {
var id = document.querySelector('#app').getUserId();
var uname = document.querySelector('#app').getUsername();
if ((typeof id === 'undefined') || (typeof uname === 'undefined')) {
this.routeTo('login');
}
var that = this;
var rootRef = new Firebase('https://<FIREBASE>.com/erfassung/' + id);
rootRef.on('value', function(snapshot) {
that.zeiteintraege = [];
snapshot.forEach(function(child) {
var zeintrag = child.val();
that.push('zeiteintraege', zeintrag); //THIS IS ALL!!!
});
});
},
ready: function() {
this.zeiteintraege = [];
}
});
})();
</script>

Not sure whether you are looking for something similar:
<dom-module id="my-zeiteintrag-list">
<template>
<template is="dom-repeat" items="zeiteintraglist">
<div>
<span>{{item.name}}</span><br />
<span>{{item.country}}</span>, <span>{{item.phone}}</span>.<br />
<span><a href$="{{generateEmailLink(item.email)}}"><span>{{item.email}}</span></a></span>
</div>
</template>
</template>
</dom-module>
<!--Script section starts -->
<script>
(function () {
Polymer({
// define element prototype here
is: 'my-zeiteintrag-list',
generateEmailLink: function(value) {
//Computed property, since 1.0 does not allow string concatenation
return "mailto:" + value;
},
ready: function () {
this.zeiteintraglist = [
{ "name": "John Doe", "country": "USA", "phone": "1 202 303 4567", "email": "jhondoe#sample.com"},
{ "name": "Sara O'coner", "country": "USA", "phone": "1 202 303 4567", "email": "sara#sample.com"}
];
}
});
})();
</script>

I'm glad to see you found the solution. I'd like to touch base on the why a little bit. Polymer data binding is built on events for efficiency. When a value is mutated or replaced, Polymer sends events to components that are bound to that data, and they update. Mutating the array directly does not fire these events, because arrays aren't aware of polymer. Hence, polymer provides it's own version of push, pop, shift, unshift, and splice, because these versions fire the correct event before updating the array.
It's important to keep this in mind with data binding in polymer in general. If you have an object, and you modify properties on that object outside of polymer (so not using things like this.set), you will have to manually notify Polymer of the path that updated so that templates rendered off of that object can know to update.

Related

AngularJs-Google-Maps Markers not displaying

I'm confused because the AngularJs and HTML code is near exact to a project where this works, but this is also my first time using LAMP instead of MEAN/MERN so maybe it's something to do with that?
my HTML file is:
<div ng-controller="map-controller">
<ng-map center="35.5951,-82.5515" zoom="12" on-click="vm.placeCheckpoint(data)">
<!-- Place marker for each checkpoint -->
<marker id='{{checkpoint._id}}'
ng-repeat="checkpoint in vm.checkpoints"
position="{{checkpoint.position}}"
on-click="vm.showDetail(checkpoint)"
>
</marker> <!-- this doesn't display -->
<marker position="35.5951,-82.5515"></marker> <!--this displays -->
</ng-map>
</div>
and map-controller.js is:
(function(window, angular, undefined) {
angular.module('map')
.controller('map-controller', ['NgMap', '$window', 'mapService',
function(NgMap, $window, mapService) {
var vm = this;
// ==================== Map =====================================
// Display map
NgMap.getMap().then(function(map) {
vm.map = map;
});
// Populate map with checkpoints
mapService.getCheckpoints().then(function(data) {
vm.checkpoints = data;
console.log(vm.checkpoints); // logs as a list of objects
});
}])
})(window, window.angular);
About the only differences between this the server, variable names, and that Google is making me use an API key for this whereas it wasn't requiring it for the other. They're both hitting the same API to get the data.
Also, if I try adding an directive, the map disappears.
First of all, make sure position property in vm.checkpoints array has the proper format [lat,lng], for example:
[
{
"name": "Oslo",
"position" : [ 59.923043,10.752839 ]
},
{
"name": "Stockholm",
"position" : [ 59.339025, 18.065818 ]
}
]
Secondly, vm.checkpoints is undefined in marker directive, you need to change expression ng-controller="map-controller" with ng-controller="map-controller as vm"
Example
(function (window, angular, undefined) {
angular.module('map', ['ngMap'])
.factory('mapService', function ($rootScope, $http) {
var mapService = {
getCheckpoints: function () {
return $http.get('https://rawgit.com/vgrem/a171e20cbe9915707e5b94c139105a65/raw/europe.json').then(function (response) {
return response.data;
});
}
}
return mapService;
})
.controller('map-controller', ['NgMap', '$window', 'mapService',
function (NgMap, $window, mapService) {
var vm = this;
vm.checkpoints = [];
// Display map
NgMap.getMap().then(function (map) {
vm.map = map;
});
// Populate map with checkpoints
mapService.getCheckpoints().then(function (data) {
vm.checkpoints = data.map(function(item, idx){
var position = [item.position.lat,item.position.lng];
item._id = idx;
item.position = position;
return item;
});
console.log(vm.checkpoints); // logs as a list of objects
});
vm.showDetail = function(){
console.log('clicked')
}
}])
})(window, window.angular);
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<script src="https://rawgit.com/allenhwkim/angularjs-google-maps/master/build/scripts/ng-map.js"></script>
<div ng-app="map" ng-controller="map-controller as vm">
<ng-map center="48.1049441,4.1858258" zoom="4" >
<marker
ng-repeat="checkpoint in vm.checkpoints"
position="{{checkpoint.position}}"
on-click="vm.showDetail(checkpoint)" >
</marker>
</ng-map>
</div>

Can't get RubaXa/angular-legacy-sortablejs to move items between lists

Can someone tell me why this plunk will not allow me to move items between the two lists?
I am able to get shared lists to work using the plain RubaXa Sortable library and plain Javascript, but I have not been able to get them to work with Angular and RubaXa/angular-legacy-sortablejs library.
I have read and re-read the docs on the configuration options here and I swear I am doing it correctly.
I have also reviewed the example from the docs (not allowed to link it here due to low rep points) with no success.
I have created two lists and connected them using identical config info:
var ctrl = this;
ctrl.sortableConf = {
group: {
name: 'tags',
pull: true,
put: true
},
sort: true,
animation: 150,
draggable: '.list-group-item',
filter: '.js-remove',
chosenClass: ".sortable-chosen"
};
They both sort just fine internally, I just can't drag an item from one to the other or vice versa.
The documentation is wrong, or I don't know how to properly reference it when not using a partial page instead of an embedded template.
After debugging the options-loading code in sortable.js, I realized that it was not loading the group: block from the ctrl.sortableConf and I was getting stuck with the default values:
After trying a ton of different ways to do this, I stumbled on this example and was able to work it out form there.
Here is a plunk and a copy of the code just in case that goes away:
// create angular app
var tagsApp = angular.module('tagsApp', ['ng-sortable']);
tagsApp.controller('bugTagController', ['$scope', function($scope) {
$scope.tags = [
'Beans',
'Potatoes'
];
$scope.bugTagControllerConfig = {
group: 'tags',
pull: true,
put: true,
sort: true,
animation: 150,
draggable: '.list-group-item',
filter: '.js-remove',
chosenClass: ".sortable-chosen",
accept: function(sourceItemHandleScope, destSortableScope) {
console.log('masterTagController:accept');
return true;
},
onStart: function(evt) {
console.log('masterTagController:onStart');
},
onEnd: function(evt) {
console.log('masterTagController:onEnd');
},
onAdd: function(evt) {
console.log('masterTagController:onAdd');
},
onRemove: function(evt) {
console.log('masterTagController:onAdd');
},
onFilter: function(evt) {
var el = masterTags.closest(evt.item); // get dragged item
el && el.parentNode.removeChild(el);
console.log('masterTagController:onFilter');
},
onSort: function(evt) {
console.log('masterTagController:onSort');
var $item = $(evt.item);
var id = $item.data('id');
if (evt.action === 'add') {
console.log('masterTagController:add');
// put a check to make sure it's unique
// check to see if this node has already been added and prevent it it has
var itemCount = evt.item.parentNode.children.length;
for (var i = 0; i < itemCount; i++) {
var $child = $(evt.item.parentNode.children[i]);
var childId = $child.data('id');
if (childId === id && i !== evt.newIndex) {
console.log('masterTagController:rejectedNewItem');
evt.preventDefault();
return;
}
}
if (evt.newIndex === itemCount - 1) {
Sortable.utils.swap(evt.item.parentNode, evt.newIndex, evt.newIndex - 1);
}
}
}
};
}]);
tagsApp.controller('masterTagController', ['$scope', function($scope) {
$scope.tags = [
'Apples',
'Oranges',
'Comquats',
'Bannanas',
'Pineapples'
];
$scope.masterTagControllerConfig = {
group: 'tags',
pull: true,
put: true,
sort: true,
animation: 150,
draggable: '.list-group-item',
filter: '.js-remove',
chosenClass: ".sortable-chosen",
accept: function(sourceItemHandleScope, destSortableScope) {
console.log('masterTagController:accept');
return true
},
onStart: function(evt) {
console.log('masterTagController:onStart');
},
onEnd: function(evt) {
console.log('masterTagController:onEnd');
},
onAdd: function(evt) {
console.log('masterTagController:onAdd');
},
onRemove: function(evt) {
console.log('masterTagController:onAdd');
},
onFilter: function(evt) {
var el = masterTags.closest(evt.item); // get dragged item
el && el.parentNode.removeChild(el);
console.log('masterTagController:onFilter');
},
onSort: function(evt) {
console.log('masterTagController:onSort');
var $item = $(evt.item);
var id = $item.data('id');
if (evt.action === 'add') {
console.log('masterTagController:add');
// put a check to make sure it's unique
// check to see if this node has already been added and prevent it it has
var itemCount = evt.item.parentNode.children.length;
for (var i = 0; i < itemCount; i++) {
var $child = $(evt.item.parentNode.children[i]);
var childId = $child.data('id');
if (childId === id && i !== evt.newIndex) {
console.log('masterTagController:rejectedNewItem');
evt.preventDefault();
return;
}
}
if (evt.newIndex === itemCount - 1) {
Sortable.utils.swap(evt.item.parentNode, evt.newIndex, evt.newIndex - 1);
}
}
}
};
}]);
And here is the html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div ng-app="tagsApp">
<!-- Starting Secondary Tags -->
<div class="col-md-2">
<h2>Tags on this list</h2>
<div class="well" ng-controller="bugTagController">
<ul id="bugTags" class="list-group" ng-sortable="bugTagControllerConfig" ng-model="tags" style="well-lg">
<li class="list-group-item" ng-repeat="tag in tags" ng-sortable-item style="well-lg">
<div ng-sortable-item-handle>{{ tag }}</div>
</li>
</ul>
</div>
</div>
<!-- Ending Secondary Tags -->
<!-- Starting Master Tags -->
<div class="col-md-2">
<h2>Master Tag List</h2>
<div class="well" ng-controller="masterTagController">
<ul id="masterTags" class="list-group" ng-sortable="masterTagControllerConfig" ng-model="tags" style="well-lg">
<li class="list-group-item" ng-repeat="tag in tags" ng-sortable-item style="well-lg">
<div ng-sortable-item-handle>{{ tag }}</div>
</li>
</ul>
</div>
<!-- Ending Master Tags -->
</div>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-route.js"></script>
<script type="text/javascript" src="https://rubaxa.github.io/Sortable/Sortable.js"></script>
<script type="text/javascript" src="ng-sortable.js"></script>
<script src="script.js"></script>
</body>
</html>

Polymer neon-animation: Element is remains visible after animating

I downloaded the Polymer Starter Kit and am trying to animate an paper-element like so
<section data-route="contact">
<button on-click="_onButtonClick">Toggle</button>
<my-dialog>
<paper-material elevation="1">
<h2 class="page-title">Contact</h2>
<p>This is the contact section</p>
</paper-material>
</my-dialog>
</section>
my-dialog.html as follows:
<dom-module id="my-dialog">
<template>
<content></content>
</template>
</dom-module>
<script>
Polymer({
is: 'my-dialog',
behaviors: [
Polymer.NeonAnimationRunnerBehavior
],
properties: {
opened: {
type: Boolean
},
animationConfig: {
type: Object,
value: function() {
return {
'entry': [{
name: 'slide-left-animation',
node: this
}],
'exit': [{
name: 'fade-out-animation',
node: this
}]
}
}
}
},
listeners: {
'neon-animation-finish': '_onAnimationFinish'
},
_onAnimationFinish: function() {
if (!this.opened) {
this.style.display = '';
}
},
show: function() {
this.opened = true;
this.style.display = 'inline-block';
this.playAnimation('entry');
},
hide: function() {
this.opened = false;
this.playAnimation('exit');
}
});
</script>
The problem I'm facing is that after toggling the animation, my paper-element is squished and remains visible on screen. How do I make it not visible after animation? I've tried hardcoding <paper-material hidden?=true> but that also does not hide the element.
As commented, you simply need to change this.style.display = 'none';

I'm looking for a way to take the hard coded "Character" data in my Angular app and load it from a separate json file

I'm looking for a way to take the hard coded "Character" data in my Angular app and load it from a separate json file.
I have a controller for the ($http) thats worked in other apps, I'm just not sure how to strip, pull and access these character names and properties from a JSON file. Any help would be appreciated.
<body>
<div class="container">
<div ng-app="polarisApp">
<h1>The Other Guys</h1>
<h3>Custom Events in Nested Controllers</h3>
<div ng-controller="Characters">
<div class="lList"> <span ng-repeat="name in names" ng-click="changeName()">{{name}}</span>
</div>
<div class="cInfo">
<div ng-controller="Character">
<label>Name:</label>{{currentName}}
<br>
<label>Job:</label>{{currentInfo.job}}
<br>
<label>Weapon:</label>{{currentInfo.weapon}}
<br> <span ng-click="deleteChar()">Delete</span>
</div>
</div>
</div>
</div>
<script src="http://code.angularjs.org/1.3.0/angular.min.js"></script>
<script src="angular.js"></script>
<script>
angular.module('polarisApp', [])
.controller('Characters', function ($scope) {
$scope.names = ['Alan', 'Terry', 'Gene', 'Sheila', 'Danson', 'Highsmith', 'Bob'];
$scope.currentName = $scope.names[0];
$scope.changeName = function () {
$scope.currentName = this.name;
$scope.$broadcast('CharacterChanged', this.name);
};
$scope.$on('CharacterDeleted', function (event, removeName) {
var i = $scope.names.indexOf(removeName);
$scope.names.splice(i, 1);
$scope.currentName = $scope.names[0];
$scope.$broadcast('CharacterChanged', $scope.currentName);
});
})
.controller('Character', function ($scope) {
$scope.info = {
'Alan': {
weapon: 'Calculator',
job: 'Police Officer'
},
'Terry': {
weapon: 'Gun',
job: 'Police Officer'
},
'Gene': {
weapon: 'None',
job: 'Police Captain'
},
'Sheila': {
weapon: 'None',
job: 'M D'
},
'Danson': {
weapon: 'Gun',
job: 'Police Detective'
},
'Highsmith': {
weapon: 'Gun',
job: 'Police Detective'
},
'Bob': {
weapon: 'None',
job: 'Police Accountant'
}
};
$scope.currentInfo = $scope.info['Alan'];
$scope.$on('CharacterChanged', function (event, newCharacter) {
$scope.currentInfo = $scope.info[newCharacter];
});
$scope.deleteChar = function () {
delete $scope.info[$scope.currentName];
$scope.$emit('CharacterDeleted', $scope.currentName);
};
});
</script>
</body>
This is the ($http) controller I wrote.
angular.module('polarisApp')
.controller('MainCtrl', function ($scope, $http) {
$http.get('character.json')
.success(function(data) {
$scope.characterStatus = data.caracterStatus;
});
You can try this
var info = null;
$http.get('character.json').success(function(data) {
info = data;
});
The response from the $http.get request will be the object contained in content.json file. If you need to access Alan's job, you can use info.Alan.job and so on.
I got it working with this controller:
App.controller('CharacterCtrl', function($scope, $http) {
$http.get('characters.json')
.then(function(res){
$scope.characters = res.data;
}); });
Thank you very much for your feedback. I haven't seen that variable you used in any similar controllers. I think I should look into it--Might be missing out on a better way to $http. Thanks.

Multiple custom filters in angular.js

I have a multi check box application which requires me to have multiple filters. I have been unable to use multiple filters even if I try to hard code an array to filter on. Here is an example I have created to try to make this work.
Working Example
HTML MARKUP:
<body ng-app="app">
<div ng-controller="MainCtrl">
<div ng-repeat="item in data.sessions | IndustryFilter : data.sessions.industry ">
{{item.id}}
</div>
</div>
Javascript
var app = angular.module("app", [])
.controller("MainCtrl", function ($scope, MatchedFilterList) {
$scope.data = {"sessions": [{
"id": "a093000000Vhzg7AAB",
"industry": ["Automovtive"],
"sessionName": "Keynote",
},
{
"id": "a093000000zg7AAB",
"industry": ["Automovtive", "Retail"],
"sessionName": "Keynote2",
},
{
"id": "a093er000f00zg7AAB",
"industry": ["Automovtive", "Retail", "Consumer Goods"],
"sessionName": "Keynote3",
}
]};
}).filter("IndustryFilter", function (MatchedFilterList) {
return function () {
var filtered = [];
angular.forEach(MatchedFilterList.industry, function (item) {
filtered.push(item);
});
console.log("Filter: Filter " + filtered)
return filtered;
};
})
.factory("MatchedFilterList", function(){
var matchedFilterList = {};
matchedFilterList.industry = {
"Automotive": "Automotive",
"Retail" : "Retail"
};
return matchedFilterList;
});

Resources