No function calls happening in my polymer element - polymer-1.0

I have created an element to list an array of companies (listado-empresas)
This is my code:
<link rel="import" href="ficha-empresa.html">
<dom-module id="listado-empresas">
<template>
<style>
:host {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
</style>
<p hidden="{{hayempresas}}">NingĂșn registro</p>
<p>{{nroEmpresas}} resultados</p>
<template is="dom-repeat" items="[[empresas]]" as="empresaAct">
<ficha-empresa empresa="[[empresaAct]]"></ficha-empresa>
</template>
</template>
<script>
Polymer({
is: 'listado-empresas'
, properties: {
empresas: Array
, hayempresas: {
type: Boolean
, computed: 'hayempresas()'
}
, nroEmpresas: {
type: Number,
computed: 'nroEmpresas()'
}
}
, hayempresas: function () {
console.log("funcion hayempresas...");
return nroEmpresas > 0;
}
, nroEmpresas: function(){
console.log("funcion nroEmpresas ...");
return this.empresas.length;
}
});
</script>
</dom-module>
The element list the companies successfully, but no function call is happening (because I don't see any log message)
Is there anything wrong in my code?
Thanks!

You have several problems:
Functions associated to computed fields shouldn't be named equal as the field. In your case both fields have the same name as the function that computes the function. Remember that this is javascript, you can't have a function and a field named the same in an object.
hidden: As it is an attribute you must use $= to enable correct data binding(When you use only = it will bind the property not the attribute)
Computed properties should observe one or more fields/paths. As stated on the documentation:
Computed properties are virtual properties computed on the basis of one or more paths. The computing function for a computed property follows the same rules as a complex observer, except that it returns a value, which is used as the value of the computed property.
Therefore, there shouldn't be a function with no parameters asociated to a computed field. For example, by your code, I guessed that nroEmpresas observe empresas and hayempresas do observe the field nroEmpresas.
Also as the field empresas is observed we must provide a default initial value in order to execute the first computation (you can assign null, [] ,undefined as long as you initialize the value).
I've updated your code with the previous adjustments
<link rel="import" href="ficha-empresa.html">
<dom-module id="listado-empresas">
<template>
<style>
:host {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
</style>
<p hidden$="{{hayempresas}}">NingĂșn registro</p>
<p>{{nroEmpresas}} resultados</p>
<template is="dom-repeat" items="[[empresas]]" as="empresaAct">
<ficha-empresa empresa="[[empresaAct]]"></ficha-empresa>
</template>
</template>
<script>
Polymer({
is: 'listado-empresas'
, properties: {
empresas: {
type: Array,
value: []
}
, hayempresas: {
type: Boolean
, computed: '_hayempresas(nroEmpresas)'
}
, nroEmpresas: {
type: Number,
computed: '_nroEmpresas(empresas)'
}
}
, _hayempresas: function (nroEmpresas) {
console.log("funcion hayempresas...");
return nroEmpresas > 0;
}
, _nroEmpresas: function(empresas){
console.log("funcion nroEmpresas ...");
return empresas.length;
}
});
</script>
</dom-module>
A plunker with part of the code running (Note that the plunker example doesn't have ficha-empresa, is just the portion of the code on the question) can be found here: http://plnkr.co/edit/wdn0cSIcNNKkzyLd6EhF?p=preview

Related

Vue - Update rendered elements from a v-for with :style that uses their values [duplicate]

I'm looping through elements and I'm positioning div using top and left CSS properties:
<div
v-for="coord in coords"
:style="{ top: coord.y + 'px', left: coord.x + 'px' }"
></div>
Sometimes instead of top property I need to use bottom (this depends on one of my Vuex store values). How can I dynamically define if I should use top or bottom CSS property?
I tried to used computed prop isTopOrBottom which would return 'top' or 'bottom: :style="{ isTopOrBottom: coord.y + 'px', left: coord.x + 'px' }". But this is not working in Vue.
You can use the ternary operator (in case computed properties are not working)
For example:
<span
class="description"
:class="darkMode ? 'dark-theme' : 'light-theme'"
>
Hope this help.
It should be like JavaScript string concatenation
<div
v-for="coord in coords"
:style="'top: '+coord.y + 'px;left: '+coord.x + 'px'"
></div>
Vue.config.productionTip = false;
Vue.config.devtools=false;
var app = new Vue({
el: '#app',
data:{
coords:[{y:10,x:10},{y:20,x:20},{y:30,x:30}]
}
});
.border-line{
border: 1px solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<div id="app">
<div class="border-line"
v-for="coord in coords"
:style="'margin-top: '+coord.y + 'px;margin-left: '+coord.x + 'px'"
>Test</div>
</div>
You could do something like this:
:class="{ is-top: isTop, is-bottom: isBottom }"
And in your script:
computed() {
isTop() {
// return top condition
},
isBottom() {
// return bottom condition
}
}
Handle css:
.is-top {
...
}
.is-bottom {
...
}
You can also use a Vuejs custom directive for this! https://v2.vuejs.org/v2/guide/custom-directive.html
Check this out:
In your template:
<p v-position="expressionThatMakesItTop" v-if="isSignedIn">Welcome back {{user.email}}</p>
If you want to register a directive locally, components also accept a directives option, check that out on the documentation I linked.
I am going to show you how to do it globally so in your main.js file, before constructing the Vue instance of course:
I left the console.log that displays the objects that you can use in your directive so you can explore them on your console and tailor this to your needs.
Vue.directive("position", {
bind: function(el, binding, vnode) {
console.log(el, binding, vnode);
el.style.left = `${vnode.context.coord.x}px`;
if (binding.value) {
el.style.top = `${vnode.context.coord.y}px`;
return;
}
el.style.bottom = `${vnode.context.coord.y}px`;
}
});

Apply rowStyleClass to every row in PrimeReact DataTable

I have data and each row/user is formatted something like this:
{
first: <string>
active: <bool>
}
I wish to apply a background color to the entire row if active property is false. Currently I have this, to try to get style applied to every row
rowClassName = (rowData) => {
return {'greyed' : true}; //will be {'greyed': !rowData.active} but this is for demonstration
}
<DataTable value={this.props.users.toJS()} //in render
selectionMode="single"
selection={user}
onSelectionChange={this.props.dispatch.editAccount}
rowClassName={this.rowClassName}
>
<Column field="first" header="First" filter={true}/>
</DataTable>
.greyed{ //in css
background-color: red;
}
which is only applying the style to every other row (see picture)
Any ideas on what I should try? i posted this question on the primeFaces forum 3 days ago and never got a response: https://forum.primefaces.org/viewtopic.php?f=57&t=58605
I just ran into this problem while trying out PrimeReact. My issue turned out to be that the default selector that sets the row background was more specific than my own.
This is the default:
body .p-datatable .p-datatable-tbody > tr:nth-child(even) {
background-color: #f9f9f9;
}
so just having
.specialRowColor { background-color: 'blue' }
is not specific enough to override the default. Instead I needed to do this in my css:
.p-datatable .p-datatable-tbody .specialRowColor {
background-color: blue;
}
Solved by overriding the css like this
.ui-datatable tbody > tr.ui-widget-content.greyed {
background-color: #808080;
}

ng-repeat active class with different style

I am creating a list of offers with ng-repeat. Depending of each offer status, they should have different colors and, when active, it should have a different specific status as well. The active logic works well, but what is happening now, is that they all render as true, so they're all the same color. Feel free if you have any other ideas of doing this.
This is what I see when I inspect, after it renders:
ng-class="{'offer card-active-false card row text-left': currentOfferId === offer.id, 'offer card card-false row text-left': currentOfferId !== offer.id}" class="offer card card-true row text-left"
Here is what I have on HTML:
<div ng-repeat="offer in $parent.offersList track by $index">
<button ng-click="$ctrl.setCurrentOffer(offer)">
<div ng-if="" ng-class="{'offer card-active-{{offer.status}} card row text-left': currentOfferId === offer.id, 'offer card card-{{offer.status}} row text-left': currentOfferId !== offer.id}">
//then I have my divs
</div>
</button>
</div>
CCS:
.card-true {
background-color: #00FF44;
}
.card-false {
background-color: #C4C4CC;
}
.card- {
background-color: yellow;
}
.card-active-true {
background-color: #fff!important;
border-color: #00FF44;
}
.card-active-false {
background-color: #fff!important;
border-color: gray;
}
.card-active- {
background-color: #fff!important;
border-color: yellow;
}
thanks!
Put classes that always need to be present in a normal class attribute.
Then simplify the classes you need and separate them so you don't have to over-complicate the logic. My suggestions may be off, but it should look something like this:
card-status-... - driven by offer.status
card-active - driven by `currentOfferId === offer.id'
Then you could easily put the logic in ngClass, which lets you specify an array whose members can be strings that represent class names or objects whose keys are class names and whose boolean values indicate whether the class should be included. Like so:
<div class="offer card row text-left"
ng-class="[
'card-status-' + offer.status,
{'card-active' : currentOfferId === offer.id}
]">
Now in your CSS you can set up those classes by combining selectors:
.card {
background-color: yellow;
}
.card-status-true {
background-color: #00FF44;
}
.card-status-false {
background-color: #C4C4CC;
}
.card.card-active {
background-color: #fff !important;
border-color: yellow;
}
.card.card-active.card-status-true {
background-color: #fff !important;
border-color: #00FF44;
}
.card.card-active.card-status-false {
background-color: #fff !important;
border-color: gray;
}
Here is my solution, remove the complicated logic, which is obviously not binding properly inside the ng-class, it will only confuse and its not worth the time.
Note: I have used $scope variables instead of this, please use the GIST of the JSFiddle I'm sharing and try to build your code, I am unsure of the color requirements, please check and tell me if the code resolves your issue.
JSFiddle Demo
CODE:
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
$scope.offersList = [{id:1, status: false}, {id:2, status: false}, {id:3, status: false}, {id:4, status: false}];
$scope.currentOfferId = 0;
$scope.setCurrentOffer=function(index){
$scope.currentOfferId = $scope.offersList[index].id;
$scope.offersList[index].status = !$scope.offersList[index].status;
}
$scope.filterClass = function(offer){
var bool = offer.status ? 'true' : 'false';
if($scope.currentOfferId === offer.id){
return 'offer card-active-'+bool;
}else{
return 'offer card card-'+bool;
}
}
});
It's not completely clear which classes you want applied to which situation. You need to hand ng-class an array with each separate condition, this was probably the root of your problem.
You can also use ternary for this (angular v.1.1.4+ introduced support for ternary operator) which makes things look a little neater:
<div ng-class="[offer.id ===currentOfferId ? 'card-active-true' : 'card-active-false',
offer.status ? 'card-active-true' : 'card-false' ]"
class="offer card row text-left" >

change text color from directive when innerHTML matches

I have a page where I am rendering many columns with the help of the ng-repeat.
HTML
<div ng-repeat="col in selectedColumn" class="cellHldr ng-status-color">{{lead[col.name]}}</div>
Now I have made a directive ngStatusColor
.directive('ngStatusColor', function () {
return {
restrict: "C",
compile: function (tElement, tAttributes) {
return {
post: function postLink( scope, element, attributes ) {
console.log("element",element[0].innerHTML );
if(element[0].innerHTML=='Open'){
console.log("hellooo");
}
}
}
}
}
});
I need to color the text of that column which has the {{lead[col.name]}}like 'open','closed'. Rest should be left as it is
Since you are going the innerHtml way , you can just add a font tag to the inner html to change the color :
if(element[0].innerHTML=='Open'){
element[0].innerHTML="<font color='red'>"+element[0].innerHTML+"</font>";
}
The angular way to do this would be using ng-class.On your div which put the following :
ng-class="{ 'red': lead[col.name]=='Open', blue: lead[col.name]=='close' }"
And create two css classes red and blue:
.red {
color: red;
}
.blue {
color: blue;
}
What this will do this is when it will assign red class to your element if the value of lead[col.name] becomes 'Open' , Blue if 'Close'.
If you want to get more info about ng-class , I would recommend you to go through this link :ng-class uses
Use ng-class for the same.
CSS
.colorChange {
color: green;
}
HTML
<div ng-repeat="col in selectedColumn" class="cellHldr ng-status-color" ng-class="lead[col.name] === 'open' ? 'colorChange': 'normalStyle'">{{lead[col.name]}}</div>
Will this approach work ?
if(element[0].innerHTML=='Open'){
element[0].style.color = "red";
}

How can I animate the movement of remaining ng-repeat items when one is removed?

I have a dynamic list of items using ng-repeat. When something happens an item may disappear. I have handled smoothly animating the removal of these items using ng-animate, but after they are gone, the remaining items simply snap to their new position. How can I animate this movement smoothly?
I've tried applying an "all" transition to the repeated class and using ng-move with no success.
You can achieve this by animating the max-height property. Check out this sample:
http://jsfiddle.net/k4sR3/8/
You will need to pick a sufficiently high value for max-height (in my sample, I used 90px). When an item is initially being added, you want it to start off with 0 height (I'm also animating left to have the item slide in from the left, as well as opacity, but you can remove these if they don't jibe with what you're doing):
.repeated-item.ng-enter {
-webkit-transition:0.5s linear all;
-moz-transition:0.5s linear all;
-o-transition:0.5s linear all;
transition:0.5s linear all;
max-height: 0;
opacity: 0;
left: -50px;
}
Then, you set the final values for these properties in the ng-enter-active rule:
.repeated-item.ng-enter.ng-enter-active {
max-height: 90px;
opacity: 1;
left: 0;
}
Item removal is a bit trickier, as you will need to use keyframe-based animations. Again, you want to animate max-height, but this time you want to start off at 90px and decrease it down to 0. As the animation runs, the item will shrink, and all the following items will slide up smoothly.
First, define the animation that you will be using:
#keyframes my_animation {
from {
max-height: 90px;
opacity: 1;
left: 0;
}
to {
max-height: 0;
opacity: 0;
left: -50px;
}
}
(For brevity, I'm omitting the vendor-specific definitions here, #-webkit-keyframes, #-moz-keyframes, etc - check out the jsfiddle above for the full sample.)
Then, declare that you will be using this animation for ng-leave as follows:
.repeated-item.ng-leave {
-webkit-animation:0.5s my_animation;
-moz-animation:0.5s my_animation;
-o-animation:0.5s my_animation;
animation:0.5s my_animation;
}
Basics
In case anyone is struggling with figuring out how to get AngularJS animations to work at all, here's an abbreviated guide.
First, to enable animation support, you will need to include an additional file, angular-animate.js, after you load up angular.js. E.g.:
<script type="text/javascript" src="angular-1.2/angular.js"></script>
<script type="text/javascript" src="angular-1.2/angular-animate.js"></script>
Next, you will need to load ngAnimate by adding it to the list of your module's dependencies (in the 2nd parameter):
var myApp = angular.module('myApp', ['ngAnimate']);
Then, assign a class to your ng-repeat item. You will be using this class name to assign the animations. In my sample, I used repeated-item as the name:
<li ng-repeat="item in items" class="repeated-item">
Then, you define your animations in the CSS using the repeated-item class, as well as the special classes ng-enter, ng-leave, and ng-move that Angular adds to the item when it is being added, removed, or moved around.
The official documentation for AngularJS animations is here:
http://docs.angularjs.org/guide/animations
TLDR: Jank is bad, do animations with transform. Check out this fiddle for css and demo.
Explanation
Note that animating height, max-height, top, ... is really bad performance wise because they cause reflows and thus jank (more information on html5rocks|high-performance-animations).
There is however a method getting this type of animation using only transforms by utilizing the sibling selector.
When elements are added there is one reflow because of the new item, all items below are transformed up so they stay at the same position and then the transformation is removed for a smooth slide-in.
In reverse when elements are removed they are transformed to the new position for a smooth slide-out and when the element is finally removed there is again one reflow and the transform is removed instantly so they stay at their position (this is also why it is important to only have transition set on ng-animate).
Alternatively to the example you could also do a transform: scaleY(0) on the deleted item and only transform: translateY() the siblings.
Caveat
Note that this snippet has trouble when multiple elements are removed in quick succession (before the previous animation has completed).
This can be fixed by having an animation time faster than the time a user takes to delete another item or by doing some more work on the animation (out of scope of this answer).
Finally some code
Note: apparently SO breaks the demo with multiple deletes - check out the fiddle to see it in work.
angular.module('app', ['ngAnimate'])
.controller('testCtrl', ['$scope', function($scope) {
var self = this;
self.items = [];
var i = 65;
for(; i < 72; i++)
{
self.items.push({ value: String.fromCharCode(i) });
}
self.addItem = function()
{
self.items.push({ value: String.fromCharCode(i) });
i++;
}
self.removeItemAt = function(index)
{
self.items.splice(index, 1);
}
}])
li
{
height: 48px;
width: 300px;
border: 1px solid lightgrey;
background-color: white;
position: relative;
list-style: none;
}
li.ng-enter,
li.ng-enter ~ li {
transform: translateY(-100%);
}
li.ng-enter.ng-enter-active,
li.ng-enter.ng-enter-active ~ li {
transform: translateY(0);
}
li.ng-animate {
z-index: -1;
}
li.ng-animate,
li.ng-animate ~ li {
transition: transform 0.6s;
}
li.ng-leave,
li.ng-leave ~ li {
transform: translateY(0);
}
li.ng-leave.ng-leave-active,
li.ng-leave.ng-leave-active ~ li {
transform: translateY(-100%);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.23/angular-animate.js"></script>
<div ng-app="app" ng-controller="testCtrl as ctrl">
<ul>
<li ng-repeat="item in ctrl.items" ng-bind="item.value">
</li>
</ul>
<button ng-click="ctrl.addItem()">
Add
</button>
<button ng-click="ctrl.removeItemAt(5)">
Remove at 5
</button>
</div>

Resources