I got a Ember.Handlerbars.JSON helper, which formats a given value to an indented JSON string.
I'd like to set the content (value) of a textarea like this:
{{#view Ember.TextArea}}
{{JSON someValue}}
{{/view}}
This does not work, since what I should set the textareas' "value" attribute instead.
However, this also doe not work
{{view Ember.TextArea valueBinding="JSON someValue"}}
You could solve this by using a computed property, see http://jsfiddle.net/pangratz666/3A33H/:
Handlebars:
<script type="text/x-handlebars" >
{{#with App.jsonController}}
{{view Ember.TextArea valueBinding="formatted" rows="10" }}
{{/with}}
</script>
JavaScript:
App = Ember.Application.create({
formatJSON: function(obj) {
return JSON.stringify(obj, null, '\t');
}
});
App.jsonController = Ember.Object.create({
content: {
abc: 123,
foo: 'hello'
},
formatted: function() {
var obj = this.get('content');
return App.formatJSON(obj);
}.property('content')
});
Update to your comments:
In the fiddle in the comment ( http://jsfiddle.net/4QNur/ ) your are declaring {{view Ember.TextArea valueBinding="JSON App.someComplexValue"}}: this does not work since valueBinding takes a path as argument and not an expression, like JSON App.someComplexValue. If you want to bind to a transformed value, just create a computed property and bind to this. That's the Ember way of doing such stuff...
In your original question you have the following code:
{{#view Ember.TextArea}}
{{JSON someValue}}
{{/view}}
This does not work in this case, since the value for the Ember.TextArea can only be set via a value respectively valueBinding:
{{view Ember.TextArea valueBinding="App.controller.transformedComplexValue" }}
Related
If I create an observer for all changes on a structure object, the observer will get called unless the the binding is a change to a value in a computed binding.
Is this the expected behavior? If so, how can I capture changes to the property in the computed binding?
Example:
<link rel="import" href="../../bower_components/paper-input/paper-input.html">
<dom-module id="binding-test">
<template>
<paper-input label="Not computed" value="{{myObject.prop1}}"></paper-input>
<paper-input label="Computed" value="{{computeIt(myObject.prop2)}}"></paper-input>
</template>
<script>
Polymer({
is:"binding-test",
properties: {
myObject: {
type: Object,
notify: true,
value: {
prop1: 1,
prop2: 2
}
}
},
observers: [
'somethingChanged(myObject.*)'
],
somethingChanged: function(changeRecord) {
// This code is never executed when the Computed input field is changed
console.log(changeRecord);
},
computeIt: function(value) {
return value;
}
});
</script>
</dom-module>
I could be wrong with this one but I think computed binding is one-way, same as computed property.
If you really want to notify the change on a paper-input like that, you can listen to the value-changed event and then do a notifyPath/set on "myObject.prop2".
<paper-input label="Computed" on-value-changed="valueChanged" value="{{computeIt(myObject.prop2)}}"></paper-input>
valueChanged: function(e) {
this.set("myObject.prop2", e.detail.value);
}
Check out this plunker.
Update
I think there's a better solution for your problem. Instead of converting values back and forth using expressions/filters, paper-input now allows you to define prefix and suffix like the following -
<paper-input label="revenue" type="number">
<div prefix>$</div>
</paper-input>
<paper-input label="email">
<div suffix>#email.com</div>
</paper-input>
You can even define complex inputs like this (you will need to create your own date-input element though) -
<paper-input-container auto-validate>
<label>Social Security Number</label>
<ssn-input class="paper-input-input"></ssn-input>
<paper-input-error>SSN invalid!</paper-input-error>
</paper-input-container>
Code samples above are taken from here. You can read more about it on Polymer's official website over here.
There're some times when we could need adding a custom element dynamically into a context. Then:
The inserted polymer could receive some properties bound to another
property inside the context, so it can change accordingly.
At polymer 0.5 we could use PathObserver to binding a property to a
context property for a recently added component. However, I did not
find a workaround or equivalent at polymer 1.0.
I have created an example for 0.5 and just the same for 1.0. See below the code of the polymer that it makes the injection. Also you can see the full plunker examples for clarity.
Ej 0.5:
<polymer-element name="main-context">
<template>
<one-element foo="{{foo}}"></one-element>
<div id="dynamic">
</div>
</template>
<script>
Polymer({
domReady: function() {
// injecting component into polymer and binding foo via PathObserver
var el = document.createElement("another-element");
el.bind("foo", new PathObserver(this,"foo"));
this.$.dynamic.appendChild(el);
}
});
</script>
</polymer-element>
Please, see the full plunkr example: http://plnkr.co/edit/2Aj3LcGP1t42xo1eq5V6?p=preview
Ej 1.0:
<dom-module id="main-context">
<template>
<one-element foo="{{foo}}"></one-element>
<div id="dynamic">
</div>
</template>
</dom-module>
<script>
Polymer({
is: "main-context",
ready: function() {
// injecting component into polymer and binding foo via PathObserver
var el = document.createElement("another-element");
// FIXME, there's no a path observer: el.bind("foo", new PathObserver(this,"foo"));
this.$.dynamic.appendChild(el);
}
});
</script>
Please, see the full plunkr example: http://plnkr.co/edit/K463dqEqduNH10AqSzhp?p=preview
Do you know some workaround or equivalent with polymer 1.0?
Right now, there is no direct way to do it. You should manually do the double binding by listening to changes in the foo property of the parent element and listening to the foo-changed event of the programmatically created element.
<script>
Polymer({
is: "main-context",
properties: {
foo: {
type: String,
observer: 'fooChanged'
}
},
ready: function() {
var self = this;
var el = this.$.el = document.createElement("another-element");
//set the initial value of child's foo property
el.foo = this.foo;
//listen to foo-changed event to sync with parent's foo property
el.addEventListener("foo-changed", function(ev){
self.foo = this.foo;
});
this.$.dynamic.appendChild(el);
},
//sync changes in parent's foo property with child's foo property
fooChanged: function(newValue) {
if (this.$.el) {
this.$.el.foo = newValue;
}
}
});
</script>
You can see a working example here: http://plnkr.co/edit/mZd7BNTvXlqJdJ5xSq0o?p=preview
Unfortunately I think it's not possible to do this by a "clean" way. To replace the Path Observer, we have to add link on the "foo" value changes to the dynamic elements. The first step is observe the "foo" property value changes. The second step is replicate the changes to each dynamic elements created.
<dom-module id="main-context">
<template>
<one-element foo="{{foo}}"></one-element>
<div id="dynamic">
</div>
</template>
</dom-module>
<script>
Polymer({
is: "main-context",
// Properties used to make the link between the foo property and the dynamic elements.
properties: {
foo: {
type: String,
observer: 'fooChanged'
},
dynamicElements: {
type: Array,
value: []
}
},
ready: function() {
// injecting component into polymer and binding foo via PathObserver
var el = document.createElement("another-element");
// Keeps a reference to the elements dynamically created
this.dynamicElements.push(el);
this.$.dynamic.appendChild(el);
},
// Propagates the "foo" property value changes
fooChanged: function(newValue) {
this.dynamicElements.forEach(function(el) {
el.foo = newValue;
});
}
});
</script>
See the full Plunkr example: http://plnkr.co/edit/TSqcikNH5bpPk9AQufez
I am almost there!
I would like to show a specific div based on the selected value.
For example, if selected value = "Option1" is chosen, then show corresponding div.
I've tried the following in the data-bind:
data-bind="if"selectedValue() === 'Option1'"
I am successfully able to use data-bind="visible" selectedValue" to toggle, but both divs show. I would like to get at the object property itself.
Here's the code:
<select data-bind=", options: availableValues, optionsText: 'Name', value: selectedValue, optionsCaption: 'Select One'"></select>
<div data-bind="if:selectedFieldType() === 'Option1' ">
#Html.Partial("_Edit" + this.Model.Type)
</div>
<div data-bind="if:selectedFieldType() === 'Option2' ">
#Html.Partial("_Add" + this.Model.Type)
</div>
$(function () {
var testModel = {
availableValues: ko.observableArray(#Html.Json(Model.SelectedValueOptions)),
selectedValue: ko.observable(null))
};
testModel.value= ko.dependentObservable(function () {
if (this.selectedValue()) {
return this.selectedValue().Val;
}
}, testModel);
var tryGetValue = $.grep(testModel.availableSelectedValues(), function (item) {
return item.Val === '#Model.Value';
})[0] || null;
testModel.selectedValue(tryGetValue);
ko.applyBindings(testModel, $('#general-section')[0]);
});
Thanks to Artem's comment, I was able to fix it - I was referencing if:value()...and needed to reference the value referenced in the model as opposed to the name I used in the binding parameters (e.g. testModel.value - value was what I needed to reference (in my case that was xyzType as opposed to the generic term "value" that was used in the data-bind options.
I was trying to write a simple directive to generate a (potentially) more complex dom element. I am quite confused about what is going on here but I think the directive I use inside my directive get linked first? Anyway the element I am generating is not visible where it should.
Sorry for all that confusion, here is the plunkr:
http://plnkr.co/edit/vWxTmA1tQ2rz6Z9dJyU9?p=preview
I think the directive I use inside my directive get linked first?
Yes. A child directive's link function will execute before the parent's link function.
Here is a fiddle that shows two nested directives,
<div d1>
<div d2></div>
</div>
and it logs when the directives' controller and link functions are called.
There are a few issues with your Plunker:
Since you are using # for your isolate scopes, you need to use {{}}s in your attribute values:
<visible value='{{visible}}'>plop</visible>
<invisible value='{{visible}}'>plop</invisible>
Since $scope.visible is defined in your controller, I assume you meant to use that value, and not test.
In the invisible directive, you need to use isolate scope property value in your link function. Property visible is available to the transcluded scope (which is in affect if you use a template in your directive like #Langdon has) but not the isolate scope, which is what the link function sees.
var template = "<span ng-show='value'>{{value}}</span>";
Plunker.
If you want a simple directive, you're better off letting Angular do most of the work through ngTransclude, and $watch.
http://plnkr.co/edit/xYTNIUKYuHWhTrK80qKJ?p=preview
HTML:
<!doctype html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>trying to compile stuff</title>
<script src="http://code.angularjs.org/1.1.1/angular.js"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="AppCtrl">
<input type="checkbox" ng-model="test" id="test" /><label for="test">Visibility (currently {{test}})</label>
<br />
<br />
<visible value='test'>visible tag</visible>
<invisible value='test'>invisible tag</invisible>
</div>
</body>
</html>
JavaScript:
angular
.module('app', [])
.controller('AppCtrl', function($scope) {
$scope.test = false;
})
.directive('visible', function() {
return {
restrict: 'E',
transclude: true,
template: '<span ng-transclude></span>',
replace: true,
scope: {
value: '='
},
link: function(scope, element, attrs) {
console.log(attrs);
scope.$watch('value', function (value) {
element.css('display', value ? '' : 'none');
});
console.log(attrs.value);
}
};
})
.directive('invisible', function() {
return {
restrict: 'E',
transclude: true,
template: '<span ng-transclude></span>',
replace: true,
scope: {
value: '='
},
link: function(scope, element, attrs) {
scope.$watch('value', function (value) {
element.css('display', value ? 'none' : '');
});
}
};
});
I know how to remove text in a simple html textbox but html textareas seem much more complicated. instead of the value attribute you put the text right between:
<html>
<textarea> </textarea>.
</html>
This is why im having trouble making an onFocus and onBlur event.
<textarea name="message" onfocus="if(this.value==this.defaultValue)this.value='';" onblur="if(this.value=='')this.value=this.defaultValue;">
Put anything for default value here
</textarea>
Live example: http://jsfiddle.net/SRYLg/
A textarea behaves like other <input> elements (with type text or password), instead of having a value attribute, the value is between the <textarea> and </textarea> tags.
Accessing and modifying the contents of the textfield is no difference. The below code displays a textarea and an input box. The same function is used for accessing the values and modifying it. If the value equals to "example text" when entering the input, the text is cleared. If the textarea / input box is empty when leaving it, "example text" will be put in it.
<textarea id="field1">example text</textarea>
<input id="field2" value="example text">
<script>
function addEvents(id) {
var field = document.getElementById(id);
field.onfocus = function () {
if (this.value == "example text") {
this.value = "";
}
};
field.onblur = function () {
if (this.value == "") {
this.value = "example text";
}
};
}
addEvents("field1");
addEvents("field2");
</script>
Live example
Your Javascript should have:
function RemoveText(obj)
{ obj.value = ''; }
And Your HTML element should have:
onfocus="RemoveText(this);"
what about calling a javascript function during the onFocus event?
function emptyText(){
document.getElementById(textarea).innerHTML = "";
}