I have a polymer element which is my parent element for my entire app. It performs ajax calls routinely and sets values according to the AJAX calls.
Within the polymer element I have a child element that needs to display one of these values dynamically.
Here is the child code:
<polymer-element name="main-status" attributes="auto">
<template>
<div id="status" flex>
<div id="info" middle>
<p>Robot is in {{ auto ? "AUTO" : "MANUAL T1" }} mode.</p>
</div>
</div>
<link rel="stylesheet" type="text/css" href="main-status.css">
</template>
<script src="main-status.js"></script>
</polymer-element>
It is used in the parent as such:
<main-status auto="{{statusAuto}}"></main-status>
And {{statusAuto}} is confirmed to change (between true and false). This change does not get reflected in the child element. I have similarly tried binding to global variables using the monostate pattern suggested on polymer's website, and I still have no success.
How can this be achieved?
<main-status auto="{{statusAuto}}></main-status>
misses the closing quote - should be
<main-status auto="{{statusAuto}}"></main-status>
If that is only in the question not in your actual code try
<main-status auto="{{statusAuto}}">{{statusAuto}}</main-status>
and verify that statusAuto updates the value properly.
Related
Demo
Demo fixed accordingly to accepted answer
Consider a component, let's call it <simple-dialog>, with this template:
<button type="button" (click)="visible = !visible">TOGGLE</button>
<div *ngIf="visible">
<ng-content select="[main]"></ng-content>
</div>
I omit the component TypeScript definition cause it's basically the same as the one generated by ng-cli.
Then I use it like this:
<simple-dialog>
<div main>
<app-form></app-form>
</div>
</simple-dialog>
When i first click the button the child component is rendered; if I click the button again the child component is removed from the DOM.
The problem is that, at this point, app-form's ngOnDestroy is not called.
I'm new to angular, so I am not sure whether my expectation is wrong.
What you are trying to achieve is called conditional content projection.
In your case, by using ng-content, the components are instantiated by the outer component, unconditionally - the inner component just decides not to display it.
If you want conditional instantiation, you should pass a template instead:
<simple-dialog>
<ng-template>
<div main>
<app-form></app-form>
</div>
</ng-template>
</simple-dialog>
and use an #ContentChild annotated property to access
the template from the content within the SimpleDialogComponent:
#ContentChild(TemplateRef, { static: false })
content!: TemplateRef<unknown>;
which can then be rendered in the template as
<div *ngIf="visible">
<ng-container [ngTemplateOutlet]="content"></ng-container>
</div>
You can also read about this here:
https://angular.io/guide/content-projection#conditional-content-projection
I am using polymer 1.
I have used something like this in polymer and I am passing the section's content from the point where i use this component.
It's working fine for chrome and firefox.
But for IE-11, I am not getting anything for the content.
<section id="asdf" class="className">
<content select="section"></content>
</section>
Does someone know the about this issue ?
Try setting the select attribute value to contain a period. The select attribute needs a selector not a string.
<template>
<header>Local dom header followed by distributed dom.</header>
<content select=".content"></content>
<footer>Footer after distributed dom.</footer>
</template>
Polymer docs
In Polymer 1.0+, how do you pass in a template from the light dom to be used in the dom-module? I'd like the template in the light dom to have bind variables, then the custom element use that template to display its data.
Here is an example of how it would be used by the component user:
<weather-forecast days="5" as="day">
<template id="forecast-template">
<img src="{{day.weatherpic}}">
<div>{{day.dayofweek}}</div>
<div>{{day.wordy}}</div>
<div>{{day.highlowtemp}}</div>
</template>
</weather-forecast>
This weather-forecast component would contain an iron-list in the dom-module and ideally, the component would reference the "forecast-template" inside the iron-list that would bind the variables to the element. Ultimately, it would generate a 5-day forecast using that template. My issue is that I haven't seen an example of bind-passing variable based templates into a Polymer 1.0 custom element. I would think this or something similar would be fairly commonplace.
The following is an example of how I would like to see it work on the client side, but no luck yet. Although the template is successfully references, it only displays once, while other fields actually do show in the loop.
<dom-module id="weather-forecast">
<template is="dom-bind">
<iron-list items="[[days]]" as="day">
<content selector='#forecast-template'"></content>
</iron-list>
</template>
</dom-module>
Any input is appreciated. Thanks!
You cannot use dom-bind inside another polymer element.
Your first element should just be dom-bind
<template is="dom-bind">
<iron-list items="[[days]]" as="day">
<weather-forecast day=[[day]]></weather-forecast>
</iron-list>
</template>
Your second element should be weather-forecast
<dom-module id="weather-forecast">
<template>
<style></style>
<img src="{{day.weatherpic}}">
<div>{{day.dayofweek}}</div>
<div>{{day.wordy}}</div>
<div>{{day.highlowtemp}}</div>
</template>
<script>
Polymer({
is: "weather-forecast",
properties: {
day: {
type: Object
}
}
});
</script>
</dom-module>
If this does not work, try wrapping the weather-forecast tag inside a template tag inside iron-list.
You can use the Templatizer. You can look at my blog for some example (there's also a Plunker link).
Unfortunately there seems to be some bug or limitation, which breaks two way binding:
Add a "dynamic" element with data-binding to my polymer-element
Polymer: Updating Templatized template when parent changes
Two way data binding with Polymer.Templatizer
I'm trying to build a WebComponent where you can edit items in an array, with the Polymer javascript framework. Model to DOM bindings work OK, but DOM to Model doesn't - simplified example:
<polymer-element name="rep-test">
<template>
<template repeat="{{item in items}}">
<input type="text" value="{{item}}" placeholder="changes don't work!">
</template>
<button on-click="{{add}}">Add</button>
{{items}}
</template><script>
Polymer({
ready: function() { this.items = [] },
add: function() { this.items.push('') },
itemsChanged: function() { console.log(this.items) } // debug
})
</script>
</polymer-element>
<rep-test></rep-test>
The items are correctly displayed in the input elements, but when I change the value inside an input, changes are not reflected to the model (items). The binding works only in one direction.
Is there any way to make the binding bidirectional, so that when a change occur in the DOM, it is copied in the model ?
I've seen this todo demo which achieves this effect, but it does so with custom events associated with items changes. This obviously works but I'm looking for a more declarative way of doing this with bindings.
Since changes in array’s elements are not reflected to itemsChanged, I would suggest you to listen on the input changes:
<!-- ⇓⇓⇓⇓⇓⇓⇓⇓⇓ -->
<input type="text" on-change="{{ itemChanged }}"
value="{{item}}" placeholder="changes don't work!">
[...]
<!-- inside script -->
itemChanged: function(e) {
console.log(e.path[0].value)
}
Below is the link to the working example: http://plnkr.co/edit/sZYHeMuAVB0G1muHhFNK?p=preview
Here is an example of bidirectional binding: as you change the values in the input fields model is updated:
Plunk
Follow data changes:
<br>
{{testData.employees[0].firstName}}
<br>
{{testData.employees[3].firstName}}
<br><br>
<template repeat="{{person in testData.employees}}">
{{person.firstName}}
<input type="text" value="{{person.firstName}}">
<br>
</template>
I'll reference this post because it explains how this works better then I can:
"...if you change the data values, the new values are NOT available to all other instances - because the instance variables are just copies of the referenced strings. By using an object with data properties, as in the edited version above, and only ever reading from and assigning to the data properties of that object rather than overwriting the object itself, changed values are shareable between instances."
Using Polymer, I am attempting to instantiate several ajax-service elements using template binding, <template repeat=...>.
Code is as follows:
<template repeat="{{viewName, i in views}}">
<section hash={{viewName}} layout vertical center-center on-tap={{closeOpenDrawer}}>
<core-ajax id="ajaxService" auto response={{list}}" url="../componentsItems/demo-components.json"></core-ajax>
<template repeat="{{element, j in list}}">
<workspace-elem class="dropped" name="{{element.name}}"></workspace-elem>
</template>
</section>
</template>
The problem is, each ajax response is concatenated to a shared list variable, rather then instantiating its own local list variable per repeated template, so when the sub template is triggered, it generates <workspace-elem>s in each section for the sum of data from all ajax calls.
Is there an easy way to solve this? Is there something I am over looking?
EDIT:
Same sort of problem occurs with the inner template. Each instantiated inner template shares the list variable, if anything is pushed to template.model.list, all instantiated template models are updated.
When you use response={{list}} in the template, you are not creating that variable, but you are binding the value of the response attribute to an existing variable called ‘list’.
The question you need to ask yourself is: ‘Where does the “list” variable that I'm binding to come from?’ It is not obvious from the snippet you mention, but it's very likely that it's coming from some enclosing custom element, so it's only natural that it will be shared between the iterations of the template (though I'm surprised that it get concatenated instead of overwritten…). I think a good solution would be to encapsulate the code you have in the outer template in a custom element to hold the variable:
<polymer-element name="my-element" attributes="viewName">
<template>
<!--Your original code starts here-->
<section hash={{viewName}} layout vertical center-center>
<core-ajax id="ajaxService" auto response={{list}}" url="../componentsItems/demo-components.json"></core-ajax>
<template repeat="{{element, j in list}}">
<workspace-elem class="dropped" name="{{element.name}}"></workspace-elem>
</template>
</section>
<!--Your original code ends here-->
</template>
<script>
Polymer({
publish: {
list: null
},
created: function() {
// Create your array on instantiation of an element
this.list = [];
}
}
</script>
</polymer-element>
<template repeat="{{viewName, i in views}}">
<my-element on-tap={{closeOpenDrawer}}></my-element>
</template>
I think that should solve your problem. Alternatively, I think it might help to make the outer template an auto-binding template(‘is="auto-binding"’). Then the model of the template would be the template itself, but since I have not used this facility very often, I'm not quite sure (it might be that you're then loosing the ability to bind to ‘views’).