Polymer 1.0: Refresh/Re-Render Computed Value Without Property Binding - polymer-1.0

I'm trying to re-compute and render a value within a Polymer 1.0 template. However, I'm trying to do this without binding to any properties.
In case it matters, the use case is for a translation mechanism that uses a string key to find the translated value. When the 'translations' value changes, the translate() call needs to be re-computed.
The component definition is as follows :
<dom-module id="my-component">
<template>
<style></style>
<p><span>[[translate("SOME_STRING")]]</span></p>
</template>
<script>
var MyComponent = Polymer({
is: "my-component",
properties: {
translations: {
type: Object,
notify: true,
value: {
"SOME_STRING": "Some String"
}
}
},
translate: function (key) {
if (this.translations.hasOwnProperty(key)) {
return this.translations[key];
}
}
});
</script>
</dom-module>
I can get the refresh to work by adding the translations property to the translate() call as follows :
<p><span>[[translate("SOME_STRING", translations)]]</span></p>
However, what I would like to do is re-compute/refresh without having to put the translations property as a second parameter in every call (there's other reasons too).
Basically, when the translations object updates with different locale translations, I'd like the translate("SOME_STRING") to be re-computed.
Is this possible? Is there any way to re-render the template or even just re-render the entire component manually? How? If not, what is the simplest way to get the computed value or template re-rendered without a property in the binding?

How about that????
<dom-module id="my-component">
<template>
<style></style>
<p><span>[[str]]</span></p>
</template>
<script>
var MyComponent = Polymer({
is: "my-component",
properties: {
translations: {
type: Object,
notify: true,
value: {
"SOME_STRING": "Some String"
},
observer: '_Changed'
},
str: {
type: String,
value: "hello"
}
},
_Changed: function(){
this.set("str",this.translate(this.str));
},
translate: function (key) {
if (this.translations.hasOwnProperty(key)) {
return this.translations[key];
}
}
});
</script>

Related

Binding an array in polymer and detecting changes in a child component

I have a component in Polymer that has an array as one of its properties.
Then I have a child component to which I'm passing this array. What I'm trying to do is to detect a change in that array from the child component.
Here is the parent component:
<dom-module id="some-test">
<template>
<paper-input label="Let's update param" on-input="updateParam"></paper-input>
<some-child param={{param}}></some-child>
</template>
<script>
Polymer({
is: 'some-test',
properties: {
param: {type: Array, value: []}
},
updateParam: function(e) {
this.param["test"] = e.currentTarget.value;
console.log("Param has been updated");
}
});
</script>
</dom-module>
And here is the child component:
<dom-module id="some-child">
<template>
<div>the child component</div>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
param: {type: Array, observer: "doSomething"},
},
doSomething: function() {
console.log("ah, we've detected a change in param");
},
});
</script>
</dom-module>
So, when I type something in the input field (in the first component), I get a consolge log of "Param has been updated". All good.
But then I would except param to be passed to the child component, which would detect the change and log "ah, we've detected a change in param".
However this doesn't seem to happen. If I use a string instead of an array, then it works as intended. But if it's an array and I update one of its values, the child component doesn't pick up the change.
This leads me to believe I am missing something as to how to bind an array. Any clues?
You can use complex observers in child components:
<dom-module id="some-child">
<template>
<div>the child component</div>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
param: {type: Array, observer: "doSomething"},
},
//Here add complex observers
observers: ['doSomething(param.*)', 'doSomething(param.splices)'],
doSomething: function() {
console.log("ah, we've detected a change in param");
},
});
</script>
</dom-module>
Ah, I finally got it working! It took a combination of the various answers. Here is the final code that works:
Parent component:
<dom-module id="some-test">
<template>
<paper-input label="Let's update param" on-input="updateParam"></paper-input>
<some-child param={{param}}></some-child>
</template>
<script>
Polymer({
is: 'some-test',
properties: {
param: {type: Array, value: []}
},
updateParam: function(e) {
this.set("param.test", e.currentTarget.value); <!-- this is the first trick -->
console.log("Param has been updated");
}
});
</script>
</dom-module>
And here is the child component:
<dom-module id="some-child">
<template>
<div>the child component</div>
</template>
<script>
Polymer({
is: 'some-child',
properties: {
param: {type: Array},
},
observers: [
"doSomething(param.*)"
]
doSomething: function() {
console.log("ah, we've detected a change in param");
},
});
</script>
</dom-module>
So, the two things that I needed to do were:
1) Use "this.set()" to update the value of the object in the parent component
2) Use a complex observer in the child component

Polymer Properties order ... matters?

I am finding very odd behavior of a Polymer element when defining properties of that element... specifically Arrays and Objects.
I have a dom-repeat template displaying this data Array, to give some context.
For example:
//.... Template definition
<script>
Polymer({
is: 'sample-element',
properties:{
user: {
type:Object,
notify: true,
},
data:{
type: Array,
value: function(){
return [
{'name':"Facebook", 'website': "http://www.facebook.com"},
{'name':"Twitter", 'website':"http://www.twitter.com"},
{'name':"Google",'website':"http://www.google.com"}];
},
}
}
});
</script>
This renders incorrectly, however if i switch the order of these properties, data is defined as an Array with 3 Objects when the template renders.
For example:
//.... Template definition
<script>
Polymer({
is: 'sample-element',
properties:{
data:{
type: Array,
value: function(){
return [
{'name':"Facebook", 'website': "http://www.facebook.com"},
{'name':"Twitter", 'website':"http://www.twitter.com"},
{'name':"Google",'website':"http://www.google.com"}];
},
},
user: {
type: Object,
notify: true,
},
}
});
</script>
Any clue why this works for defining the Object last?
Console shows no errors in sample-element either...
If you are using default values the order indeed matters.
Maybe user tries to access the value of data which is not set yet.

How do I pass MVC model data to AngularJS controller scope

In my application I'm returning the view below which uses this TestViewModel.
public class TestViewModel
{
public List<string> Tests { get; set; }
}
View:
#model AdminSite.Models.TestsViewModel
#{
ViewBag.Title = "Tests";
}
<hgroup class="title">
<h1>#ViewBag.Title</h1>
</hgroup>
<!doctype html>
<html>
<head>
<script src="~/Scripts/Angular/angular.min.js"></script>
<script src="~/Scripts/Angular/Tests.js"></script>
</head>
<body>
<div ng-app="testsTable">
<div ng-controller="TableController">
<table my-table options="options"></table>
</div>
</div>
</body>
</html>
As you can see I'm using AngularJS to create a DataTable, what I would like though is that instead of the table data "aaData" being hardcoded I want it to be from TestViewModel model.
var app = angular.module('testsTable', []);
app.directive('myTable', function () {
return {
restrict: 'A',
link: function (scope, element, attrs, controller) {
var dataTable = element.dataTable(scope.options);
},
scope: {
options: "="
}
};
});
app.controller('TableController', ['$scope', function ($scope) {
$scope.options = {
aoColumns: [
{
"sTitle": "Col 1",
},
{
"sTitle": "Col 2"
},
{
"sTitle": "Col 3"
}
],
aoColumnDefs: [{
"bSortable": true,
}],
bJQueryUI: true,
bDestroy: true,
aaData: [
["A", "B", "C"],
["A", "B", "C"],
]
};
}]);
I think I may have to create another directive to bind the model e.g
<table my-table options="options" model-data="#Model.Tests"></table>
but I'm not really sure how Angular directives work exactly, how I would write said directive & how it binds it to the scope
Your question is answered here
You could directly inject the values into JavaScript:
//View.cshtml
<script type="text/javascript">
var arrayOfArrays = JSON.parse('#Html.Raw(Model.Addresses)');
</script>
See JSON.parse, Html.Raw
Alternatively you can get the values via Ajax:
public ActionResult GetValues()
{
// logic
// Edit you don't need to serialize it just return the object
return Json(new { Addresses: lAddressGeocodeModel });
}
<script type="text/javascript">
$(function() {
$.ajax({
type: 'POST',
url: '#Url.Action("GetValues")',
success: function(result) {
// do something with result
}
});
});
</script>
See jQuery.ajax
So...Setting aaData using {{options.aaData=JSON.parse('#Html.Raw(#Model.Tests)');""}} was working but I had nothing monitoring the change meaning the table wasn't being redrawn with the new information.
to track changes I added a watch in my directive, this monitors aaData for changes then updates the table as necessary.
scope.$watch('options.aaData', handleUpdate, true);
function handleUpdate(newData) {
var data = newData || null;
if (data) {
dataTable.fnClearTable();
dataTable.fnAddData(newData);
}
}
my real solution looks a little different now as I am using dataTables ajax call to pull down my json data then setting the dataSrc with the result. Using the in-built ajax calls means I can remove the directive watch as datatables handles this for me.
One final note, if you've found yourself here while figuring out how to mix angularJS & dataTables then this fiddle has helped me a lot.
http://jsfiddle.net/TNy3w/2/
Since you're working with angular, take advantage of angular configuration:
<script>
(function () {
angular.module('app').value('mydata', {
propOne: '#Url.Action("Index", "Dirty", null)',
propTwo: angular.fromJson('#Html.Raw(Model.Addresses)')
// more properties here
});
})();
</script>
Then you can inject and use 'mydata' anywhere within your angular application
EDIT:
You can create an extension method that serializes your model to JSON, then Html.Raw won't give you an error, as long as the parameter you provide is not null.
public static class ObjectExtensions
{
public static string SerializeObject(this object obj) {
return JsonConvert.SerializeObject(
obj,
new JsonSerializerSettings {
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
}
Then do:
#using [static extension class namespace here];
...
angular.fromJson('#Html.Raw(Model.Addresses.SerializeObject())')

Polymer 1: How can I set up paper-checkbox label dynamically using a custom element

I want to set label/s of paper-checkbox elements through a custom element I have created.
This is how I am calling my custom element with the value set to a property called optionLabel which I want to display when checkbox renders on the screen.
<check-list optionLabel="My first checkbox"></check-list>
My custom element check-list looks like this:
<dom-module id="check-list">
<template>
<style>
:host {
display: block;
}
</style>
<paper-checkbox on-change="_checkChanged">{{optionLabel}}</paper-checkbox>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'check-list',
properties: {
optionLabel: {
type: String,
notify: true
}
},
_checkChanged: function (e) {
alert("State changed");
}
});
})();
</script>
</dom-module>
My goal is to reuse my custom element inside a dom-repeat layout and set values according to the requirement.
What is the correct way of doing this?
According to the documentation camelCase properties are "accessed" from outside the element like camel-case. The documentation states the following:
Attribute names with dashes are converted to camelCase property names
by capitalizing the character following each dash, then removing the
dashes. For example, the attribute first-name maps to firstName. The same mappings happen in reverse when converting property names to attribute names.
In other words, your code should have worked if you did the following instead:
<check-list option-label="My first checkbox"></check-list>
I got it to work! The variable (property) I was using previously was optionLabel, which did not work. Don't know what is the reason but when I changed it to optionlabel, i.e. all lowercase, it worked fine!
Not sure if above is the true solution to the problem I faced but it is working for me now :)
However, it will still be very helpful for many beginners like me if somebody please explain why optionLabel did not work.
So my code now changes to this
Custom element:
<dom-module id="check-list">
<template>
<style>
:host {
display: block;
}
</style>
<paper-checkbox on-change="_checkChanged">{{optionlabel}}</paper-checkbox>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'check-list',
properties: {
optionlabel: {
type: String,
notify: true
}
},
_checkChanged: function (e) {
if (e.target.checked) {
this.optionlabel = "Good to see you agree";
this.$.btnsubmit.disabled = false;
} else {
this.optionlabel = "Please agree to proceed";
this.$.btnsubmit.disabled = true;
}
}
});
})();
</script>
</dom-module>
And the call looks like:
<check-list optionlabel="My first checkbox"></check-list>

Passing down Polymer's HTML attributes to nested components

I have a custom element <x-marker> with a <paper-checkbox> inside of it.
I want an checked attribute on my element to reflect down on the <paper-checkbox> but my solution doesn't seem to be the way to do it.
HTML:
<x-marker is-checked="<%= someBoolean %>"></x-marker>
Element:
<dom-module id="x-marker">
<template>
<div>
<paper-checkbox id="checkbox"></paper-checkbox>
</div>
</template>
<script>
Polymer({
is: 'x-marker',
properties: {
isChecked: String
},
listeners: {
change: 'changeHandler'
},
attached: function() {
if (this.isChecked === 'true') {
this.$.checkbox.setAttribute('checked', this.isChecked);
}
},
changeHandler: function (event, detail, sender) {
//...
}
});
</script>
</dom-module>
In Polymer 0.5 you could use checked?="{{isChecked}}", but that doesn't seem to work in 1.0 anymore. Also hard coding <paper-checkbox checked="false"> still checks the checkbox as long as the attribute is present. The value doesn't seem to matter, that's why the attribute itself has to be bound, and not its value.
I can't seem to figure this out, including treating the property as a string === 'true' instead of a boolean, and bind it straight to the <paper-checkbox>
In 1.0 you just need to use checked="{{isChecked}}". You can implement it as follows:
HTML:
<x-marker checked="{{someBoolean}}"></x-marker>
ELEMENT:
<dom-module id="x-marker">
<template>
<div>
<paper-checkbox id="checkbox" checked="{{checked}}">Check me</paper-checkbox>
</div>
</template>
<script>
Polymer({
is: 'x-marker',
properties: {
checked: {
type: Boolean,
observer: '_checkChanged'
}
},
_checkChanged: function(newValue, oldValue) {
// Do something
}
});
</script>
</dom-module>

Resources