Ember: SafeString and htmlSafe not removing "Binding style attributes" deprecation warning - ember.js

I'm trying to figure out how to eliminate this warning
WARNING: Binding style attributes may introduce cross-site scripting vulnerabilities; please ensure that values being bound are properly escaped. For more information, including how to disable this warning, see http://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes.
Controller:
percentComplete: function() {
var percent = this.get('numProcessed') / this.get('numToDo') * 100;
return percent.toString().htmlSafe();
}.property('numProcessed', 'numToDo')
Template:
<div style="width:{{percentComplete}}"></div>
Versions:
Ember : 1.13.7
Ember Data : 1.13.8
jQuery : 1.11.3
Other attempted solutions...
I've also tried following the steps at http://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes however they don't elaborate on escapeCSS() nor does the Ember.Handlebars.SafeString seem to work from their example.
On a related note, I could see this being used often, so I first tried creating a helper with the template looking like this without success:
<div style="width:{{safe-css percentComplete}}"></div>
Helper attempts:
return Ember.String.htmlSafe(params[0]);
return Ember.Handlebars.SafeString(params[0]);
var safeInput = Ember.Handlebars.Utils.escapeExpression(params[0]);
return new Ember.String.htmlSafe(safeInput);
What am I missing here?

You cannot concatenate the style in the template:
<div style="width: {{percentComplete}}"></div>
Instead, wrap the entire style attribute in htmlSafe
<!-- Template -->
<div style={{percentCompleteCss}}></div>
// Controller
percentCompleteCss: function() {
var num = this.get('numProcessed') / this.get('numToDo') * 100;
return ('width: ' + num.toString() + '%').htmlSafe();
}.property('numProcessed', 'numToDo'),

Related

Accessing the DOM representation of a template instance that is calling a helper

I am trying to access a DOM element corresponding to the particular template instance that is calling a helper function. As I read the Meteor documentation, Template.instance() should return the template instance object that called the helper, and something like Template.instance().$() would allow me to grab DOM elements within that instance.
However, the following code (and similar variations) is not working for me:
* HTML *
<template name="input_container">
<div class="small-12 medium-12 large-6 columns empty {{isActive}}"></div>
</template>
* JS *
Template.input_container.helpers({
isActive: function() {
if (Template.instance().$('.empty') && [some Session variable logic] {
return 'active';
}
}
});
When I do something like:
if (some Session logic) {
console.log(Template.instance())
}
I get the helper properly logging multiple versions of:
Blaze.TemplateInstance {view: Blaze.View, data: 7,
firstNode: div.small-12.medium-12.large-6.columns.empty.active-container,
lastNode: div.small-12.medium-12.large-6.columns.empty.active-container, $: function…}
(With data: values going from 1-12 appropriately, but otherwise each seems to be the same)
How do I get from this to being able to used the template methods such as template.$ or template.find?
EDIT:
While not a perfect solution, I did manage to work around some of these issues by using Template.currentData() and setting an identifier on each instance of the input.
Template.create_form.helpers(
# Create 12 input containers
inputContainer: () ->
[0..11]
And then:
Template.input_container.helpers(
isActive: () ->
# Get which template instance we are working with, will return the number 0-11 that was used to create it
current = Template.currentData()
# Now I can do $(".input-container").eq(current) to grab the correct DOM element
)
But it seems a little dirty to need to use so much jQuery.
I'm not sure if I've interpreted the question correctly, but if you're trying to access another DOM element on the page - I was able to use a jquery selector.
For example, given html of
<input type="textfield" id="initials" value=" ">
and a simple meteor template of
<template name="demo">
<input type="button" div id="s0">
</template>
I can successfully access the initials field when the s0 button is clicked as follows
Template.demo.events({
'click .cell': function(event, template) {
if ($('#initials').val().trim().length > 0) {
console.log($('#initials').val().trim() + ' - you clicked button '+$(event.target).attr('id'));
}
},

EmberJS Template concatenate

How can I can concatenate strings( or how to add classes ) on templates on EmberJs?
ex.
<script type="text/x-handlebars">
// This div I want to add a class go, Is this the right way to do it?
<div class="fly {{isGo}}">Fly now</div>
// Or it's something like this?
<div class="fly "{{isGo}} >Fly now</div>
</script>
bind-attr used to be a good way of working around a limitation within Ember's rendering. Now with HTMLbars Ember has recommend that we move away from bind-attr as we have more powerful methods.
Ember 1.13 deprecated bind-attr in favor of the new syntax.
http://emberjs.com/deprecations/v1.x/#toc_bind-attr
Working example of the two proposed methods can be seen in action on ember twiddle ,here:
https://ember-twiddle.com/38f69f01d2fd994af3b0965f10882005?openFiles=templates.application.hbs%2C
Method 1
If you want to do the combination inside your handlebars template you could do something like:
<div class={{concat "fly " isGo}}>Fly now</div>
Method 2
otherwise use a computed property like:
flyingClass: Ember.computed('isGo', function() {
// return a string with 'fly' and the value of
// isGo. Will be updated if isGo changes.
// Array values are created with space delimited by
// ['className', 'anotherClassName', 'lastClastName'].join(' ');
// => "className anotherClassName lastClassName"
let going = this.get('isGo') ? 'going' : ''
return ['fly', going].join(' ');
})
and then in your handlebars template:
<div class={{flyingClass}}>Fly now</div>
The main difference between the two methods depends on how you want your separation of concerns. Right now it might be easier to just do Method 1, but as conditions get more complicated you could hide more of the work in the computed property.
There is a complete discussion of this in the Ember guide: http://emberjs.com/guides/templates/binding-element-class-names/
But you'd do it like this:
<div {{bind-attr class="isGo"}}>Fly now</div>
And in your controller:
App.MyController = Ember.ObjectController.extend({
flightIsAGo: true,
isGo: function() {
return "fly"+this.get('flightIsAGo') ? ' isGo' : '';
}.property('flightIsAGo')
}

ember version 1.6.1 best to way to bind css style

I am using ember version 1.6.1. I would like to show an error message if user doest not enter username and password correctly. I think i have to use bind-style. now I have code like this:
<fieldset class="error-message" {{bind-style visibility="isVisible:visible:hidden"}}>
<span>invalid username/password</span>
</fieldset>
what is the best way to do it ?
Ember Handlebars supports dynamic class binding exceptionally better than it does style binding. To do that you'd bind-attr to the class. http://emberjs.com/guides/templates/binding-element-class-names/
Css
.visible{
visibility:visible;
}
.hidden{
visibility:hidden;
}
Handlebars
<fieldset {{bind-attr class=":error-message isVisible:visible:hidden"}}>
<span>invalid username/password</span>
</fieldset>
Example: http://emberjs.jsbin.com/didax/1/edit
You can bind-attr the style property and create a computed property that returns the raw style text visibility:visible, but that's ugly and not necessary in this situation.
Although class is generally the best way to set these visual changes, consider using classNameBindings instead of bind-attr. That would require you to create a View class.
However, the best way to bind element attributes that don't have a specific binding mechanism, would be via attributeBindings:
(this approach also needs a View class)
App.IndexView = Ember.View.extend({
attributeBindings: ['style'],
style: function() {
return 'color: #F00';
}.property()
});
This is way is a little better because you can watch the style property of your view class and it will automatically bind to your view markup. And since that is a computed property, you can create your own code to determine changes of other attributes in your view that could cause the style attribute to be reconstructed, and again, automatically bound to your view.
You could have a property that the style property watches with property('dependency'), so when it changes, style is once again computed and the view is updated. For example, let's say that you have a view which is a custom input box with built-in validation. You have a property valid which returns boolean, being true for valid and false for invalid values.
App.IndexView = Ember.View.extend({
attributeBindings: ['style'],
valid: function() {
return false;
}.property(),
style: function() {
// these variables and all should ideally be somewhere else,
// as color codes could potentially be global for the app
var _invalidColor = "#F00";
var _validColor= "#000";
if (this.get('valid')) {
return 'color: ' + _validColor + ';';
} else {
return 'color: ' + _invalidColor + ';';
}
}.property('valid')
});
(see jsbin)
Keep in mind this is a crude example to show the functionality/possibilities. Manually change the return value of valid property of the IndexView in JS Bin to see how it affects the view template.

How to wrap text in block, using Ember js and Handlebars js?

I want to wrap some text in block. Cannot figure out how to do it.
JS:
Handlebars.registerHelper('mywrap', function (options) {
return '<p class="TEST" >' + options.fn(this) + '</p>';
});
Template:
{{#mywrap}}
This text must be wrapped
{{/mywrap}}
Final html should be like this:
<p class="TEST">This text must be wrapped</p>
Ember v1.0.0-pre4,
Handlebars 1.0.rc.2,
Jquery 1.9.0
There are lots of ways of wrapping text in a block, really depends on your usecase.
I'm assuming for some reason, literally writing the HTML is not what you're looking for.
Firstly, you can just return a Handlebars SafeString, however this presents a security issue if you are wrapping user-provided content.
Ember.Handlebars.registerHelper('mywrap', function (options) {
return new Handlebars.SafeString('<p class="TEST" >' + options.fn(this) + '</p>';)
});
Secondly, you can wrap it in a view
{{#view classNames="TEST" tagName="p"}}
This text must be wrapped
{{/view}}
Thirdly, you can create a handlebars helper that creates a view.
Ember.Handlebars.registerHelper('mywrap', function (options) {
var view = Ember.View.extend({tagName:"p",classNames:"TEST"})
return Ember.Handlebars.helper.view.call(this, view, options)
});
Caveat, I haven't actually checked the code so there may be typos

style attribute with emberjs

Is there a way to set css properties by using emberjs properties' auto binding ?
Something like:
<div {{bindAttr style="background-color: divColor;"}}>
...
</div>
Please note that as of ember 1.13. binding attributes (bind-attr) is deprecated. You would need to use code similar to this to bind to the class:
<div class={{myClass}}></div>
Furthermore, style binding in this fashion is not recommended because it can introduce XSS vulnerabilities. The HTML templating automatically escapes HTML to prevent XSS when using {{...}}, but style attributes have additional vulnerabilities outside of the scope of the built-in escaping.
The recommended approach is to escape the CSS yourself (i.e. creating the escapeCSS function that would escape the specific CSS appropriately to prevent XSS - this is not a built-in function. You could start with Ember.Handlebars.Utils.escapeExpression and add any additional checking from that base.) More information can be found here:
https://guides.emberjs.com/v2.2.0/templates/writing-helpers/#toc_escaping-html-content
Then you tell Ember that the string is "safe" by using Ember.String.htmlSafe, and Ember will not try to escape that content.
controller:
myStyle: Ember.computed('color', function() {
var color = escapeCSS(this.get('color'));
return new Ember.String.htmlSafe("color: " + color);
})
template:
<div style={{myStyle}}></div>
Reference: http://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes
Another simple way to do this is to add a computed property to your model.
Model ----
App.Photo = Em.Object.extend(
objectId: null
url: ""
style: (->
"background-image:url('" + #get("url") + "')"
).property("url")
)
Template -----
{{#each item in App.photoController}}
<div {{bindAttr style="item.style"}}></div>
{{/each}}
I got this working, and seems to be the simplest way to go about it.
Not exactly like that but close. You'll have to build the style string yourself. Look at this jsFiddle.
App = Ember.Application.create();
/**************************
* Models
**************************/
/**************************
* Views
**************************/
App.View = Ember.View.extend({
style: function() {
return "background-color:" + this.get('color');
}.property('color').cacheable()
});
/**************************
* Controllers
**************************/
App.set('controller', Ember.Object.create({
color: "transparent",
red: function() {
this.set('color', 'red');
},
blue: function() {
this.set('color', 'blue');
},
style: function() {
return "background-color:" + this.get('color');
}.property('color').cacheable()
}));
/**************************
* App Logic
**************************/
$(function() {
template:
{{#view Ember.Button target="App.controller" action="blue"}}BLUE{{/view}}
{{#view Ember.Button target="App.controller" action="red"}}RED{{/view}}
{{#view App.View colorBinding="App.controller.color" attributeBindings="style"}}
Color is {{App.controller.color}}
{{/view}}
<hr>
<div {{bindAttr style="App.controller.style"}}>And another way...</div>
Recent Ember version (2.3.0 as of this writing) allows straight-forward embedding of computed style.
// bar-graph.js
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['bar-graph'],
inlineStyle: Ember.computed('attrs.min', 'attrs.max', 'attrs.value', function() {
let min = this.get('attrs.min');
let max = this.get('attrs.max');
let value = this.get('attrs.value')
let percentage = Math.round(value / (max - min) * 100);
return new Ember.Handlebars.SafeString(`width: ${percentage}%`);
})
});
<!-- bar-graph.hbs -->
<div class="bar" style={{inlineStyle}}>{{value}}</div>
Live example
I have tried using answer provided by #WallMobile, but it has some syntax issues. So this is the correct syntax to be used.
App.Photo = Em.Object.extend({
objectId: null,
url: "",
style: function() {
return "background-image:url('" + this.get("url") + "')"
}.property("url")
})
HTMLBars now lets you do this - upgrade to the latest ember/ember-cli to take advantage of the new stuff.
There's a new addon which allows you to declare styles as JS objects and bind them to your component's style attribute. Check out ember-computed-style
import computedStyle from 'ember-computed-style';
export default Ember.Component.extend({
style: computedStyle('backgroundStyle'),
attributeBindings: ['style'],
backgroundStyle: function(){
return {
backgroundColor: this.get('divColor')
};
}.property('divColor'),
divColor: 'red'
});
This will produce:
<div style="background-color:red;"></div>
Another approach you could use is CSS custom properties.
ember-cli-custom-properties is an Ember add-on that binds component properties to CSS custom properties (variables). Its fairly simple to use. Once you install the add-on, the add-on makes the customProperties and customPropertyBindings property available on the #ember/component class.
For example, you could turn the raw HTML above into a Ember component, and give it a class name.
import Component from '#ember/component';
export default Component.extend ({
classNames: ['my-component'],
// Map the backgroundColor attribute to a CSS custom property
customProperties: ['backgroundColor']
});
You can then reference this class name in the styles in for your application.
.my-component {
background-color: var(--background-color);
}
Lastly, just set the backgroundColor attribute on the component to your desired color.
{{my-component backgroundColor="red"}}