Modify HTML within a Dom-repeat - javascript

I'm stuck on a basic thing, but which gets very complex with Polymer. I would like to change the text color of a table cell according to the value in it. I've tried using filter in dom-repeat, but it doesn't work because I don't know how to access HTML this way.
Here's sample code:
<h4>[[propertiesList.length]] Properties available</h4>
<paper-card elevation="1">
<div class="tableRow title">
<div class="title colM">Name</div>
<div class="title colL">URL</div>
<div class="title colM">Owned by</div>
<div class="title colS">Created</div>
<div class="title colM">Videos count</div>
<div class="title colM">Status</div>
<div class="title colXS">Edit</div>
</div>
<template is="dom-repeat" items="[[propertiesList]]" filter="StatusColor">
<div class="tableRow">
<div class="colM">[[item.name]]</div>
<div class="colL">[[item.url]]</div>
<div class="colM">[[item.user.name]]</div>
<div class="colS">[[item.created]]</div>
<div class="colM">[[item.videos_count]]</div>
<div class="colM" id="status">[[item.status.label]]</div>
<div class="colXS left"><paper-icon-button class="editIcon" on-tap="editProperty" icon="mdi:pencil"></paper-icon-button></div>
</div>
</template>
</paper-card>
and the JS:
StatusColor: function (item) {
if (item.status.label == "Active") {
document.getElementById("status").style.color = '#48C8B6';
console.log("Property is active");
return item.status.label;
}
},
...doesn't do anything to my text color.
Then, I've tried a good old for-loop, but for some reason, I can't get the .length value right. Here's the same HTML as above minus the filter, and "status" is now a class instead of id. The JS is as follows:
attached: function () {
this.async(function () {
var status = document.getElementsByClassName("status");
console.log("Status value : ", status);
var count = status.length;
console.log("count value : ", count);
for (i = 0; i < count; i++) {
var text = status[i].innerText;
if (text == "Active") {
status[i].style.color = "#48C8B6";
} else {
status[i].style.color = "#F1412E";
}
}
});
My first console.log that shows the status value is right. I get all my "status" divs, and the length property in Chrome Dev Tools is the right one, but the second console.log (the "count" one) always displays 0. Thus, I can't make my for-loop work properly.
Help me (...obiwan kenobi)

Note that the template filter is intended for filtering out items from your repeater (not for mapping items as you attempted to do). Also, the template repeater invokes the filter callback before stamping an item. On the first iteration, the #status node will not have been stamped yet, so document.getElementById('status') would return null (assuming no other node with ID of status exists already), resulting in a TypeError and nothing rendered for that template repeater.
Instead of the template filter or the attached callback, I recommend the following:
CSS styles for default and active statuses (keep behavioral logic in JS, and styles in CSS)
Conditionally assigned class for status div (using a computed binding with attribute binding)
As in:
// style
.status {
color: #F1412E;
}
.status.active {
color: #48C8B6;
}
// template ($= for native attribute binding)
<div class$="[[_computeStatusStyle(item.status.label)]]">[[item.status.label]]</div>
// script
Polymer({
_computeStatusStyle: function(label) {
var baseStyle = "colM status";
var activeStyle = label === "Active" ? " active" : "";
return baseStyle + activeStyle;
}
});
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="paper-card/paper-card.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<style>
paper-card {
width: 100%;
}
.tableRow {
margin: 1rem;
border-bottom: solid 1px lightgray;
}
.status {
color: #F1412E;
}
.status.active {
color: #48C8B6;
}
</style>
<template>
<h4>[[propertiesList.length]] Properties available</h4>
<paper-card elevation="1">
<template is="dom-repeat" items="[[propertiesList]]">
<div class="tableRow">
<div class="colM">[[item.name]]</div>
<div class="colL">[[item.url]]</div>
<div class="colM">[[item.user.name]]</div>
<div class="colS">[[item.created]]</div>
<div class="colM">[[item.videos_count]]</div>
<div class$="[[_computeStatusStyle(item.status.label)]]">[[item.status.label]]</div>
<div class="colXS left">
<paper-icon-button class="editIcon" on-tap="editProperty" icon="mdi:pencil"></paper-icon-button>
</div>
</div>
</template>
</paper-card>
</template>
<script>
HTMLImports.whenReady(function() {
"use strict";
Polymer({
is: 'x-foo',
properties: {
propertiesList: {
type: Array,
value: generateProperties
}
},
_computeStatusStyle: function(label) {
var baseStyle = "colM status";
var activeStyle = label === "Active" ? " active" : "";
return baseStyle + activeStyle;
}
});
/** Value generator for <x-foo>.propertiesList above */
function generateProperties() {
var props = [];
for (var i = 0; i < 5; i++) {
var statusLabel = i % 2 == 0 ? 'Active' : 'Inactive';
props.push(new Property('name', 'url', 'username', 'created', 'videoCount', statusLabel));
}
return props;
}
/** Property class for <x-foo>.propertiesList above */
function Property(name, url, username, created, videoCount, label) {
this.name = name;
this.url = url;
this.user = {};
this.user.name = username;
this.created = created;
this.videos_count = videoCount;
this.status = {};
this.status.label = label;
};
});
</script>
</dom-module>
</body>
codepen

Related

Cant get CSS property from child?

I have this HTML code here :
<div id="ctr" class="faden-slider-container">
<div class="conteneur-image" ></div>
<div class="conteneur-image" ></div>
<div class="conteneur-image" ></div>
</div>
And I am trying to get the CSS property of the second div which class' name is conteneur-image but I get nothing :
app.controller("slideCtrl",function ($scope) {
alert("hello")
var tab = new Array();
var elements = new Array();
for(var i = 0; i<3 ; i++){
elements[i] = document.getElementById("ctr").children[i]
}
var style = window.getComputedStyle(elements[1])
var message = style.getPropertyCSSValue("background").cssText()
alert("CSS Value is : "+message)
})
Couple of issues there.
getPropertyCSSValue is obsolete and may not work in browsers anymore
id is a selector, not a valid css property
Use getPropertyValue instead,
var message = style.getPropertyCSSValue("background");
Demo
var style = window.getComputedStyle(document.getElementById("ctr").children[1])
var message = style.getPropertyValue("background");
console.log("CSS Value is : " + message)
<div id="ctr" class="faden-slider-container">
<div class="conteneur-image"></div>
<div class="conteneur-image"></div>
<div class="conteneur-image"></div>
</div>
Using jquery,you can get your 2nd HTML element this way:
var element = $("#ctr").children().eq(1);
Then if you want to make some transformation, like applying style:
element.css("background-color", "blue")
Here is a snippet:
var element = $("#ctr").children().eq(1);
element.css("background-color", "blue")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="ctr" class="faden-slider-container">
<div class="conteneur-image" >one</div>
<div class="conteneur-image" >second</div>
<div class="conteneur-image" >third</div>
</div>

Detect observable change inside observableArray

There's a paragraph in knockout docs that said you can create an observableArray with properties as observables but there isn't an example of that:
http://knockoutjs.com/documentation/observableArrays.html
So what I'm trying to achieve is to add an element to an observableArray that has an observable property to detect state changes when it's clicked. So here is my code what I have so far
export class Team {
Name: KnockoutObservable<string>;
Score: KnockoutObservable<number>;
ListTeamsArray: KnockoutObservableArray<any>;
selectedTeam: KnockoutObservable<boolean>;
constructor(json) {
var self = this;
if (json !== null) {
self.Name = ko.observable(json.Name);
self.Score = ko.observable(0);
self.ListTeamsArray = ko.observableArray();
self.selectedTeam = ko.observable(false);
}
}
addTeam = () => {
var self = this;
//Working correctly and I'm declaring "selectedTeam" as an observable with initial value of "false"
var newTeam = { Name: self.Name(), Score: 0, selectedTeam: ko.observable(false)};
self.ListTeamsArray.push(newTeam);
}
//Here I create a function that is passing a "team" parameter (the one in the array and it's working fine
teamSelectedFn = (team: Team, event) => {
var self = this;
$(".teamPanel").css("background", "none");
//Loop thru array in knockout to assign selected value, probably there's a better way
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
if (item.Name === team.Name) {
$(event.currentTarget).css("background", "#a4e4ce");
item.selectedTeam = ko.observable(true);
} else {
item.selectedTeam = ko.observable(false);
}
});
//just for testing
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
console.log(item);
console.log(item.selectedTeam());
});
}
}
And this is the HTML
<div class="row" id="teamCrud">
<div class="col-sm-3" >
<div class="form-group">
<input class="form-control" data-bind="value: Name" />
#*<span data-bind="text: Score"></span>*#
<button data-bind="click: addTeam" class="btn btn-success">Add</button>
</div>
</div>
<div class="col-sm-9">
Equipos
<div data-bind="foreach: ListTeamsArray" class="row">
<div class="col-sm-3">
<div class="panel panel-default teamPanel" data-bind="click: $parent.teamSelectedFn, style: { border: selectedTeam() ? '2px solid red' : 'none'}#*, style: { background: $data.selectedTeam() ? 'red' : 'none'}*#">
<div class="panel-heading" data-bind="text: Name"></div>
<div class="panel-body">
Score:
<p data-bind="text: Score"></p>
Seleccino
<p data-bind="text: selectedTeam()"></p>
</div>
</div>
</div>
</div>
</div>
</div>
Everything it's working, I know I can change the background color of the HTML element with knockout but I need to detect the dependency change. It's not detecting the changes from the observable inside the array. Is there something else I need to do or I'm handling this the wrong way?
In your click function you are overwriting the bound observable with a new observable. You probably just need to change your function to update the existing observable instead of replacing it.
teamSelectedFn = (team: Team, event) => {
var self = this;
$(".teamPanel").css("background", "none");
//Loop thru array in knockout to assign selected value, probably there's a better way
ko.utils.arrayForEach(self.ListTeamsArray(), (item) => {
if (item.Name === team.Name) {
$(event.currentTarget).css("background", "#a4e4ce");
item.selectedTeam(true);
} else {
item.selectedTeam(false);
}
});

knockout if binding working in laptops, but not working in mobile and tablet

I am trying some thing with if binding from knockout. If value is true I want to show some text and if it is false, I want to show some different text, as given in the code.
When I am opening the page with this html, I am getting the expected results.
But when I am trying to get the result in phones and in kindle tab (working fine in wondows tab), it is not giving the results for the if binding I have used in html.
I tried removing '()' from failStatus and status in html, but it is not working. Is it any issue of binding or I am doing any thing wrong?
Thanks for any help.
function temp()
{
this.inviteeEmailList = ko.observableArray([]);
var emailList = {};
emailList['email'] = {'a#x.y , b#c.n'};
emailList['status'] = ko.observable();
emailList['failStatus'] = ko.observable();
this.showList = function()
{
for(var k in inviteeEmailList)
{
if(some_condition)
{
this.inviteeEmailList()[k]['status'](true);
this.inviteeEmailList()[k]['failStatus']("");
}
else
{
this.inviteeEmailList()[k]['status'](false);
this.inviteeEmailList()[k]['failStatus']("not exist");
}
}
}
}
<div id="foundEmail" data-bind="foreach : inviteeEmailList">
<span data-bind="if: $data.status()">
<span>Success</span>
</span>
<span data-bind="if:(!$data.status() && $data.failStatus()) ">
<span>hello world</span>
</span>
<div data-bind="text:$data.email"></div>
<div data-bind="if:!$data.status()">
<div data-bind="text:$data.failStatus()"></div>
</div><br/>
</div>
Instead of using if binding, I tried using visible binding, which worked properly for me.
Giving code below
function temp()
{
this.inviteeEmailList = ko.observableArray([]);
var emailList = {};
emailList['email'] = {'a#x.y , b#c.n'};
emailList['status'] = ko.observable();
emailList['failStatus'] = ko.observable();
this.showList = function()
{
for(var k in inviteeEmailList)
{
if(some_condition)
{
this.inviteeEmailList()[k]['status'](true);
this.inviteeEmailList()[k]['failStatus']("");
}
else
{
this.inviteeEmailList()[k]['status'](false);
this.inviteeEmailList()[k]['failStatus']("not exist");
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foundEmail" data-bind="foreach : inviteeEmailList">
<span data-bind="visible: $data.status()">
<span>Success</span>
</span>
<span data-bind="visible:(!$data.status() && $data.failStatus()) ">
<span>hello world</span>
</span>
<div data-bind="text:$data.email"></div>
<div data-bind="visible:!$data.status()">
<div data-bind="text:$data.failStatus()"></div>
</div><br/>
</div>

How to properly apply KO binding the following example

Basicaly, I have viewModel in KO having array of 2 values, I need to change css class prop of element (main) when <a> elemets are being clicked (when 1st li>a "Val1" clicked, <main class="stl1">... and so on). Strangely , nothing happends to <main>:
<script>
var mainViewModel = function () {
var self = this;
self.classArr = ['stl1', 'stl2'];
self.cssUsed = ko.observable(0);
self.getClass = function ( data, event ) {
var dat = event.target.value;
self.cssUsed = self.classArr[dat];
console.log( dat + ' : ' + self.cssUsed );
}
}
ko.applyBindings( new mainViewModel() );
</script>
<div class='page'>
<header>
<nav>
<ul >
<li>Val1</li>
<li>Val2</li>
</ul>
</nav>
</header>
<div id='maincontent'>
<main data-bind="css: cssUsed" >
<div class="center"></div>
</main>
</div>
</div>
You got it almost right. The problem was that your were assigning the value in the wrong way. Instead of
self.cssUsed = self.classArr[dat];
try
self.cssUsed(self.classArr[dat]);
Check it out here

KnockoutJS calling computed for each element in foreach

I have a problem i cannot figure out..
My example on JSbin: http://jsbin.com/wiwuwepe/1/edit
Basicly, in
<script id="QuestionTemplate" type="text/html">
<div class="well">
<div class="form-group">
<div class="col-md-5">
<p class="form-control-static" data-bind="text: QuestionText"></p>
</div>
</div>
<div class="form-group">
<div class="col-md-5">
<!-- THIS IS WHERE I WANT COMPUTED FOR EACH SURVEYQUESTION -->
<p class="form-control-static" data-bind="text: QuestionTypeTemplate"></p>
</div>
</div>
</div>
QuestionTypeTemplate shows undefinded, although model for this is
function SurveyQuestion(data) {
var self = this;
self.QuestionText = ko.observable(data.QuestionText);
self.QuestionType = ko.observable(data.QuestionType);
self.QuestionAnswers = ko.observableArray(data.QuestionAnswers);
self.QuestionTypeTemplate = ko.computed(function () {
/*
if( self.QuestionType() == 0) {
return "radio";
} else if (self.QuestionType() == 1) {
return "checkbox";
}
*/
return "This is what i want";
}, self);
}
Please, check full code on jsbin. Just uncomment that 1 line in QuestionTemplate script/html.
When I compare my example with http://knockoutjs.com/examples/cartEditor.html , I really cant find big difference, why that works, and why mine does not.
on your Survey function
instead of this
self.SurveyQuestions = ko.observableArray(data.SurveyQuestions);
you need to use this
var questions = [];
for(i = 0 ; i<data.SurveyQuestions.length ; i++) {
questions.push(new SurveyQuestion(data.SurveyQuestions[i]));
}
self.SurveyQuestions = ko.observableArray(questions);

Categories

Resources