How can I trigger a refresh of the template if a member of a data-bound complex object changes?
In the template:
<other-component data="{{complexObject}}"></other-object>
In the component:
_onChange: function(newData) {
//callback from some event-system
this.set("complexObject", newData);
}
The _onChange-Method is triggered when the complexObject is changed, but newData is always a reference to the same object, just members of this object changed - because of this, polymer doesn't update the view and doesn't pass the data down to other-component.
Is there a way to let polymer know that there is indeed some new data and it has to re-evaluate the template? It is working if I create a shallow clone of newData, but that seems like a hack and could hurt performance for big objects.
I can't use the set method to change the properties of the object via the path because the change happens outside of polymer-elements and I can't control it.
Call render()
this.$.yourTemplateID.render();
Here is one working example where sibling elements are interchanging data between each other: Plunk.
Docs: https://www.polymer-project.org/1.0/docs/devguide/data-binding.html#array-binding
<template>
<button on-tap="btnTapped">change Emploees from Second</button>
second: empl: <span>{{empl.employees.0.firstName}}</span>
</template>
...
<script>
btnTapped: function () {
console.log('Second: btnTapped');
//Propagate array subproperty
this.set('empl.employees.0.firstName', 'Test');
console.log(this.empl.employees[0].firstName);
//Object set
this.set('temp.firstName', 'Test');
}
</script>
this.complexObject = _.clone(this.complexObject)
cloning & reassigning might solve this problem`
Related
I'm wondering, is there a possibility to have databindings "out of" a template? Say I have a <template/>-Tag somewhere which I put into the slot of a different component - that component stamps it to its context. Then I want to bind data from the root element to the <template/>-Tag. Also, event bindings (on-x-changed) don't work, because you can't assign a function which is defined in the hosting component. Any ideas?
Example:
... host
{{boundData}}
<binding-component>
<template>
{{boundData}}
</template>
</binding-component>
I don't see changes when I observe boundData in the hosting component. Is there a way to get around this? Or is firing a custom event my only chance?
If you are looking for binding a property outside of polymer something like from index.html you may bind value with element. an example ; index.html
<dom-bind>
<template>
<binding-component bound-data="{{boundData}}"></binding-component>
</template>
</dom-bind>
<script>
// set a value a string, Number or Object etc.
// Optionally wrap this code into a listener ie;
// window.addEventListener('load', e=> { ...below code ... })
var boundData= document.querySelector('dom-bind');
boundData = {} //
</script>
Now in your binding-component element has a property as boundData
hope its helps or provide more code to understand better.
I've made it work the way dom-if does it, too. Like in dom-if (reference), I'm creating a Templatize-instance which then uses forwardHostProp to handle the "inside"-properties
this.__ctor = Templatize.templatize(template, this, {
mutableData: true,
forwardHostProp(prop, value) {
// handling item updates, item being the only property
// from within the binding component
// everything else is automatically bound by templatize
this.set(prop, value);
this.update(this.item);
},
});
this.__instance = new this.__ctor();
this.root.appendChild(this.__instance.root);
This all happens in connectedCallback.
Because the Templatize-instance is passed this, it's bound to the current context as well.
Good luck!
I have a Methode from an API. It returns a promise which resolves to an $ctrl(?) object. This objects should contain a measurement and will be updated whenever it receive a new data.
getMeasurements.latest(filter) //only a object to filter through all measurements
.then(function (latestMeasurement) {
$ctrl.latestMeasurement = latestMeasurement;
});
My problem is that I don't know how to work with this data or display it in my html file. How does $ctrl work?
Here the documentation of the API
$ctrl is the view model object in your controller. This $ctrl is a name you choose (vm is another most common name), if you check your code you can see the definition as $ctrl = this;, so basically its the this keyword of the controller function.
So now if you are using $ctrl.latestMeasurement = 'someValue', then its like you are adding a property latestMeasurement to controller function.
Now how to use it in HTML?
To access the latestMeasurement property in HTML your code must have <h1>{{$ctrl.latestMeasurement}}</h1> (H1 tag is just an example.)
Here $ctrl is different from what I explained above on controller part. Here $ctrl is the value used for controllerAs property of the controller. But $ctrl is the default value of the controllerAs property, so your code may not have the controllerAs property defined, so Angular will take default value $ctrl in HTML.
This is where most people gets confused. So let me explain,
Assume in your new controller you have declared your this keyword to variable vm, and you set your controllerAs property to myCtrl, i.e;
controllerAs: 'myCtrl' while defining controller properties.
var vm = this; in your controller function.
In this case in js you have to use vm for setting values, and in HTML you have to use myCtrl. For example,
in JS controller function vm.test = 'Hello world';
in HTML <span ng-bind="myCtrl.test"></span>
The result Hello world will be displayed in your page.
Why $ctrl and not $scope?
The view model object model concept is introduced in AngularJS 1.5, it is actually part of migrating to Angular 2 where $scope no longer exsist. So in 1.5 they introduced new approch but did not removed $scope completely.
Hope the answer helped.
For basic Javascript concepts you can see http://javascriptissexy.com/16-javascript-concepts-you-must-know-well/
For more detailed AngularJS $ctrl concept you can see https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
I suppose you are toking about this.
In this case, the
$ctrl.latestMeasurement
can means:
$ctrl, the controller where you are running this code. You can change it by $scope for example, and get the same result.
latestMeasurement, the variable where you want to store the last value of the measurement.
To explain my point of view let see the code below
<div ng-app="MeasurementApp">
<div ng-controller="MeasurementController">
<h1>{{latestMeasurement2}}</h1>
</div>
</div>
There you can see a simple angularjs app that shows a variable called latestMeasurement2 in a div and its controller called MeasurementController. Then, to display the value let check your code.
angular.module('MeasurementApp', [])
// creating the controller
.controller('MeasurementController', function(c8yMeasurements, $scope) {
// creating the variable and let it empty by now.
$scope.latestMeasurement2 = "";
// Your code
var filter = {
device: 10300,
fragment: 'c8y_Temperature',
series: 'T'
};
var realtime = true;
c8yMeasurements.latest(filter, realtime)
.then(function (latestMeasurement) {
// The latestMeasurement is where the measurement comes
// Here we just assign it into our $scope.latestMeasurement2
$scope.latestMeasurement2 = latestMeasurement;
});
});
As the documentation says
// $scope.latestMeasurement2 will be updated as soon as a new measurement is received.
$scope.latestMeasurement2 = latestMeasurement;
Hope this helps!
I am trying to insert data into the test fixture but couldn't achieve so far. Returns this error:
"was given a model to stamp, but the template is not of a bindable type"
My test code is like below:
<test-fixture id="myFixture">
<template is="dom-template">
<my-element given-input="[[selectedInput]]"></myElement>
</template>
</test-fixture>
<script>
suite('<my-element>', function() {
var myEl;
setup(function() {
myEl = fixture('myFixture', {selectedInput: 'test input'});
});
test('initiates my-element', function() {
// fails as givenInput returns "[[selectedInput]]"
assert.equal(myEl.givenInput, 'test input');
});
});
</script>
Similar question was asked here polymer 1.0 unit tests - how to bind properties to a child element? but the answer is not what I look for since it is directly defining target property in the child element
Also in Data binding in Polmyer's <test-fixture> it is very same issue but didn't work for me either.
My question is about, how can we pass a property down to the element through test fixture in Polymer 2.x unit testing?
After some more research, I found out that this was an already known issued which can be tracked here https://github.com/PolymerElements/test-fixture/issues/47.
The only possible workaround I have found to continue with the unit testing is to pass givenInput into myEl and removing given-input="[[selectedInput]]" from my-elemet inside test-fixture. Which is not the proper approach but at least makes testing possible.
I am working on a dashboard, in which I have a search panel at the top (let's call it component A), where users can enter a query. The value of this input will change a lot of other components in the dashboard (not only components that are its direct descendants or siblings). I want to send the search value from component A to component B, which should then respond by performing some action with the input value.
I have tried a few things:
Directly calling the function in component B. Haven't been able to get that to work at all.
Manually setting B's local property value and using an observer to trigger a function call. I manager to set the value, but the observer does not trigger.
Using a global variable, which I can easily access across components, but I still can't trigger functions in specific components.
How can I best do this?
I'm relatively new to Polymer, so forgive me if my ideas aren't completely 'Polymerised' :)
Approach 1
<dom-module id="component-B">
<template>
</template>
<script>
Polymer({
is: 'component-B',
properties: {
id: '',
observer: '_idUpdate'
},
_idUpdate: function(){
console.log("HELLO");
}
});
</script>
</dom-module>
<dom-module id="component-A">
<template>
</template>
<script>
Polymer({
is: 'component-A',
idSearch: function() {
var id = this.$.search.value;
document.querySelector('component-B').properties.id = id;
},
});
</script>
</dom-module>
As you want to send data to multiple elements (which might not be siblings of the firing element) you can use any of these two methods
Use iron-signal to fire the signal and then in all the elements where you want the data use iron-signal tag to listen to the signal
<iron-signals on-iron-signal-<signal-name>="<function>"></iron-signals>
You can also use standard HTML method dispatchEvent to fire a signal and then add eventListeners in all the element where you want data.
I'm building a new webapp and I need to know how can I pass an object between 2 custom elements in polymer.
In the code below, I set the value of mydata in "my-child-element-1" and I need to see this value in "my-child-element-2"...I think that it's not very hard to do but i'm loosing my mind to find a good solution...
In my opinion, i should create a temporary object in "my-host-element" to share the value but i'm not convinced about this...
This is my code:
<dom-module id="my-host-element">
<template>
<my-child-element-1 mydata="{{mydata}}"></my-child-element-1>
<my-child-element-2 mydata="{{mydata}}"></my-child-element-2>
</template>
<script>
Polymer({
is: "my-host-element",
properties:
{
mydata: {
type: Object
}
}
});
</script>
</dom-module>
Thank you!!
Your example looks like it should work without the host element needing a property, if the property on the child elements are set up correctly. Remember that Polymer's data binding syntax is basically syntactic sugar around firing and handling custom events. So take a look in child element 1 and make sure that you've set the property to notify when changed. For example:
Polymer({
is: "my-child-element-1",
properties: {
mydata: {
type: Object,
notify: true // fire mydata-change CustomEvent on change
}
}
});
Yes, afaik it is correct to have the parent element act as the mediator between the children, which means it needs to have its own property even if its only used for that purpose.