meteor rendering eachitem inside a style block template - meteor

I'm trying to allow personalized style setting to be persisted inside a meteor app. For sake of argument, lets say I'm keeping the values in an array of objects, each object containing a "name' and "value" attribute. When I try to render these objects inside a <style> block, Meteor instead renders a comment.
The following it my simplest Proof of Concept:
poc.html:
<head>
<title>poc</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
Styles don't render here:
<style>
body {background-color: #999;}
{{#each styles}}
.{{name}} { {{value}} }
{{/each}}
</style>
Styles render here:
<ul>
{{#each styles}}
<li class="{{name}}">{{name}} : {{value}}</li>
{{/each}}
</ul>
And here:
<div>
{{#each styles}}
.{{name}} { {{value}} } <br/>
{{/each}}
</div>
poc.js:
if (Meteor.isClient) {
Template.hello.styles= function() {
var resultArray=[];
resultArray.push( { name: 'style1', value:'color: #000'})
resultArray.push( { name: 'style2', value:'color: #fff'})
return resultArray;
}
}
The output in the style block contains:
<!--data:DuvxkGSiN6BK3M95T--><!--data:GvvkPYg2Adii4NNre-->
instead of the expected:
style1: { color: #000}
style2: { color: #fff}
Not sure if this is by design or a bug or an error in my understanding. Thanks in advance.

Meteor does some special things with markup that might be interfering with rendering inside the style tag.
There are two solutions-
if you just need to add static styles, add the following helper and render it with three braces {{{styleBlock styles}}} as a separate element:
Template.hello.styleBlock = function(styles){
content = "<style>";
_.each(styles, function(style){
content += '.' + style.name + '{' + style.value + '}';
});
content += "</style>";
return content;
};
Or if you need to dynamically add styles, you can set up an observe that finds the style sheet and calls 'insertRule'
var styleSheet = _.find(document.styleSheets,
function(sheet){return sheet.ownerNode.getAttribute("id") == 'dynamic-styles';}
);
styleSheet.insertRule('.style1{color: #000}', 0);

Related

Angularjs : Apply bold style to a character inside ng-repeat

I have a list :
$scope.list = ["test/test1/test2/test3","test3/test5/test6"];
I would like to apply bold style to / characters when displaying the list :
<div ng-repeat="path in list">
<p style="font-weight:bold">{{path}}</p>
</div>
Do you have any ideas how can I achieve this ?
Fiddle
you can do it simply with str.replace http://jsfiddle.net/k18vgtvw/
<p style="font-weight:bold" ng-bind-html-unsafe="csc(path)"></p>
controller
$scope.csc = function(path) {
return path.replace(/\//g, "<span style='color:red'>/</span>");
}
There are a number of ways to do this. First I'd add a function to your controller, let's say it's called boldSlashes.
function boldSlashes(path) {
return path.replace("/","<b>/</b>")
}
Then change your html to be:
<div ng-repeat="path in list" ng-bind-html>
boldSlashes({{path}})
</div>
The ng-bind-html tells angular to treat the contents as html and not escape it.
You also have to inject ngSanitize into you module in order to use ng-bind-html.
So wherever you create your module, add ngSanitize to the dependencies like:
angular.module('myApp',[ngSanitize])
I'm not sure if this is what you are trying to do but I separated out individual elements. Also the jsfiddle font the bold font looks exactly the same on the / character.
http://jsfiddle.net/3a2duqg4/
1. Updated the view to a list
2. Changed the array to have an individual item per section
3. Added styles to the "/" and realized the font bold property with the fiddle default font didn't look any different.
<div ng-controller="MyCtrl">
<ul>
<li class="list" ng-repeat="path in list">{{path}} <span>/</span></li>
</ul>
</div>
Added the items to a list rather than a paragraph and added some styles. I updated your array to have one value per array item as well.
Let me know if this helps! :)
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.list = ["test/test1/test2/test3","test3/test5/test6"];
$scope.updateString = function(s) {
return s.replace(/\//g, '<span class="bold">/</span>');
};
}
.bold {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="path in list">
<p ng-bind-html-unsafe="updateString(path)"></p>
</div>
</div>
</div>

How to properly find a conditionally rendered element from onRendered callback?

I’d like to find a conditionally rendered element after the template is rendered.
The template:
<template name="one">
<div class="normal">Normal</div>
{{#if active}}
<div class="conditional">Conditional</div>
{{/if}}
</template>
The code:
Template.one.onRendered(function() {
console.log(this.find(".normal"));
console.log(this.find(".conditional");
}
The above will log the ‘.normal’ but null for the ‘.conditional’ (the condition is true, both elements are present in the final DOM). The reason for that is well documented: onRender runs before {{#if}}s. But I want to run after the whole template is rendered, with all its {{#if}}s.
Is there a way to find that .conditional without making it a separate template?
I'm facing the same issue. This is because what's inside the {{#if}} block is rendered as a dynamically generated template.
So, the best workaround I've found so far is to render a specific template inside the {{#if}} block and add a callback to this specific template:
Here's what you should do:
<template name="one">
<div class="normal">Normal</div>
{{#if active}}
{{> activeTemplate}}
{{/if}}
</template>
<template name="activeTemplate">
<div class="conditional">Conditional</div>
</template>
Template.activeTemplate.onRendered(function() {
console.log(this.find(".conditional");
}
You won't find the DOM element because it doesn't exist.
It doesn't exist because active is false.
If you want active to return true:
Template.one.helpers({
"active": function() { return true; }
})

Conditionally add css class in polymer

I have an element inside a polymer component and want to add a css class conditionally.
<div class="bottom {{completed: item.completed}}">
if item.isCompleted is true, then add the .completed class.
However, I've got the following error:
Invalid expression syntax: completed?: item.completed
I don't want to put the hole subtree inside a template conditional. Is it possible to do using an expression inside an html tag? I'm using the last polymer release, is this funtionallity migrated or replaced in any way?
The tokenlist technique was valid in Polymer 0.5, but has been deprecated.
As of Polymer 1.2, the following works:
<dom-module ...>
<template>
<div class$="bottom [[conditionalClass(item.completed)]]"></div>
</template>
<script>
Polymer({
...
conditionalClass: function(completed) {
return completed ? 'completed' : '';
}
});
<script>
</dom-module>
Also see similar question: Polymer 1.0 - Binding css classes
update Polymer 1.0
<div class$="[[getClasses(item.completed)]]">
getClasses: function (completed) {
var classes = 'bottom'
if(completed) classes += ' completed';
return classes;
}
Even when completed could be read from this.item.completed inside getClasses() it's important to pass it as parameter from the binding. This way Polymer re-evaluates the function getClasses(item.completed) each time item.completed changed.
original - Polymer 0.5
It should look like
<div class="bottom {{ {completed: item.completed } | tokenList }}">
See docs for more details: http://polymer-project.org/docs/polymer/expressions.html#tokenlist
the simplest way to do it:
<template>
<style is="custom-style">
.item-completed-true { background: red; }
</style>
<div class$="bottom item-completed-[[item.completed]]"></div>
</template>

Data-binding between nested polymer elements

Suppose I have two distinct polymer-elements
One should be embedded inside the other using the content placeholder.
Is it possible to do data-binding between these two nested polymer-elements ?
I tried, but I can't get it to work: http://jsbin.com/IVodePuS/11/
According to http://www.polymer-project.org/articles/communication.html#binding data-binding between polymer-elements should work (in those examples they were done inside the template tag without using a content placeholder).
Update:
Scott Miles clarified that data-binding only works on the template level.
However in my case I don't know the exact template beforehand but I want to allow the user of my parent-element to specify which child-element it should contain (provided that there are different child-elements.
I think this question is related to this one: Using template defined in light dom inside a Polymer element
I updated the example below to highlight his:
<polymer-element name="parent-element" >
<template >
<div>Parent data: {{data1}} </div>
<content />
</template>
<script>
Polymer('parent-element', {
data1 : '',
ready: function() {
this.data='parent content';
}
});
</script>
</polymer-element>
<polymer-element name="child-element" attributes="data2">
<template>
<div>Parent data: {{data2}} </div>
</template>
<script>
Polymer('child-element', {
data2 : '',
ready: function() {
}
});
</script>
</polymer-element>
<polymer-element name="child2-element" attributes="data2">
<template>
<div>Parent data: {{data2}} </div>
</template>
<script>
Polymer('child2-element', {
data2 : '',
ready: function() {
}
});
</script>
</polymer-element>
The user can choose which child-element to embed:
<parent-element data1 = "test">
<child-element data2="{{data1}}"/>
</parent-element>
<parent-element data1 ="test" >
<child2-element data2="{{data1}}"/>
</parent-element>
Workaround:
The only workaround I found was to add change watcher and use getDistributedNodes() to get the child element and manually set data2 to data:
<polymer-element name="parent-element" >
<template >
<div>Parent data: {{data}} </div>
<content id="content"/>
</template>
<script>
Polymer('parent-element', {
data : '',
ready: function() {
this.data='parent content';
},
dataChanged : function() {
contents = this.$.content.getDistributedNodes();
if (contents.length > 0) {
contents[0].data2 = this.data;
}
},
});
</script>
</polymer-element>
Polymer data-binding works by attaching a model to a whole subtree.
You wrote:
<parent-element>
<child-element data2="{{data}}"/>
</parent-element>
this implies a rule that the parentNode provides the binding model. But now imagine you wanted to write:
<parent-element>
<div>
<child-element data2="{{data}}"></child-element>
</div>
</parent-element>
Now you have a problem.
Instead, in Polymer examples, you will notice that the {{}} are (almost always) inside of a template. For example, if I define:
<polymer-element name="host-element" attributes="data" noscript>
<template>
<parent-element data1="{{data}}">
<child-element data2="{{data}}"></child-element>
</parent-element>
</template>
</polymer-element>
Now, I have a model context (host-element) that I can use to bind things together in the entire subtree described by the template.
Note that I don't need attributes="data" for this to work. I added that so host-element exposes data and I can do this:
<host-element data="test"></host-element>
http://jsbin.com/IVodePuS/15/edit
Works like a charm. Parent element is publishing property data, that can by 2 way data bound. This way the child element gets the same data.
<polymer-element name="custom-element">
<template>
<parent data="{{data}}">
<child data="{{data}}"></child>
</parent>
</template>
<script>
Polymer({
ready: function() {
},
data: {}
});
</script>
I think in Polymer >= 1.0 this should be done using "Auto-binding template".

Meteor 0.7.1.1 handlebar conditioning in attributes

Handlebar conditioning seems not to be working in attributes.
in the .js
if (Meteor.isClient){
Template.cards.myCards = function()
{
return ["something.png"];
}
Template.card.isSelected = function()
{
return true;
};
}
in the .html
<head>
<title>test</title>
</head>
<body>
{{>cards}}
</body>
<template name="cards">
{{#each myCards}}
{{>card}}
{{/each}}
</template>
<template name="card">
<div class="{{#if isSelected}}selectedClass{{/if}}">
{{#if isSelected}}selectedContent{{/if}}
</div>
</template>
Gives me (once rendered)
<div class="<!--data:DQhTaW3zefLpaZQ2k-->">
selectedContent
</div>
Where is my "selectedClass" gone ? Why is it replaced by the commented data block ?
Take the code on GitHub
Problem recognised as a bug and issue closed the problem has been fixed in Blaze

Resources