Change template content at runtime - dart

I want to build Polymer element that generates a list output
<polymer-element name="polymer-list>
<template>
<template id="my-repeat" repeat="{{item in listData}}">
<!-- use content here -->
<!-- some fix dummy content to see how this is handled -->
<div id="div_{{item['item']['index']}}">{{item['item']['index']}}</div>
</template>
<content id="content" select="*"></content>
</template>
<script type="application/dart" src="polymer_list.dart"></script>
</polymer-element>
and I want to move the elements provided by <content select="*"></content> inside the <template id="my-repeat"> tag (at the position of <!-- use content here -->) when the polymer-list is initiated (ready, attached, ...).
My polymer-list should be used like
<polymer-list id="list" data="{{data}}" on-polymer-select="{{selectAction}}">
<div class="header">{{item['index']}}</div>
<div class="item {{item['selected']}}">Index: {{item['index']}}</div>
</polymer-list>
As result both <div>s should be repeated for each item in data by the <polymer-list> element.
I tried
var listContent = ($['content'] as ContentElement).getDistributedNodes();
var t = ($['my-repeat'] as TemplateElement);
listContent.forEach((n) => t.append(n.clone(true));
// listContent.forEach((n) => t.content.append(n.clone(true)); // didn't work either
as result I get
<!-- ... -->
<template id="my-repeat" repeat="{{item in listData}}">
#document-fragment
<div class="header"></div>
<div class="item">Index: </div>
</template>
<div id="div_0">0</div>
<div id="div_1">1</div>
<div id="div_0">2</div>
<!-- ... -->
The fix dummy content is repeated normally but the dynamically added elements are placed inside a document-fragment but not repeated.
Any hint about manipulating the HTML at <!-- use content here --> at runtime would be great.

Unfortunately you can insert you content only one time as specified in specification (http://www.w3.org/TR/2013/WD-components-intro-20130606/#insertion-points):
Insertion points let you reordered or selectively omit rendering the host's children, but they will not cause something to be rendered multiple times. Tree order dictates when each of these elements takes a pass at selecting the children. Once the child is selected to be rendered at one insertion point, it can't be claimed by another one, which is why the details-open decorator only renders the summary once.

Related

Accessing state from other web components in Dart

Let's say that you have one web component stored inside of another in Dart. Is there any way to access state from other components?
Let's say that I have a Polymer element called x-notebook-element and one called x-note-element embedded inside of it (via an iterator):
<polymer-element name="x-notebook-element">
<template>
<ul>
<template repeat="{{note in notes}}">
<li is="x-note-element" note="{{note}}"></li>
</template>
</ul>
</template>
<script type="application/dart" src="notebook.dart"></script>
</polymer-element>
The difficulty is that I want to be able to click on a Delete button within an x-note-element that removes the x-note-element from the DOM and removes the note associated with it from the notes list.
You can fire a CustomEvent in your x-note-element :
dispatchEvent(new CustomEvent('my-delete-event', detail: note.id));
And listen to those events :
<li is="x-note-element" note="{{note}}" on-my-delete-event="{{delete}}"></li>
Then in the dart part of x-notebook-element :
delete(CustomEvent e, var detail, Element target) {
final deletedId = e.detail;
// update notes
}

Rendering a content tag as part of a template in polymer and dart

I wish to make a generic list using polymer and dart. I am extending the UL element to do so. I want to place template variables within the content of this custom element.
<ul is="data-ul">
<li>{{item['first_name']}}</li>
</ul>
The custom element
<polymer-element name="data-ul" extends="ul">
<template repeat="{{item in items}}">
<content></content>
</template>
<script type="application/dart" src="data-ul.dart"></script>
</polymer-element>
I was expecting the template variable to be interpolated however it simply gets outputted to the DOM as is. How do I output the content tag to be rendered as a template and not just directly outputted?
Unfortunately, there are two issues here.
<content> cannot be used like this. It's a placeholder for rendering light DOM nodes at specific locations in the Shadow DOM. The first <content> that selects nodes, wins [1]. Stamping out a bunch like you're doing, while very intuitive, won't work as expected.
You're mixing the internal world of Polymer with the external world outside the element. What this really means is that bindings (e.g. {{}}) only work in the context of <polymer-element>.
One thing you can do is create a copy of the distributed light DOM children as the items property of your element. In JavaScript this looks like:
<template repeat="{{item in items}}">
<li>{{item['first_name']}}</li>
</template>
<content id="content" select="li"></content>
<script>
Polymer('data-ul', {
ready: function() {
this.items = this.$.content.getDistributedNodes();
}
});
</script>
Note: The only reason I've used <content select="li"> is to insure the element only takes in <li> nodes. If you're not worried about users using other types of elements, just use this.items = [].slice.call(this.children);.
To do that you should override the parseDeclaration method. This method is in charge of parsing/creating the needed html that will be bound. For example, let say that you have next template
<polymer-element name="data-ul" extends="ul" attributes="items">
<template>
<template repeat="{{item in items}}" ref="itemTemplate"></template> <!-- this is the replacement of content tag -->
</template>
<script type="application/dart" src="data-ul.dart"></script>
</polymer-element>
Or if you want to have some default elements:
<polymer-element name="data-ul" extends="ul" attributes="items">
<template>
<template repeat="{{item in items}}">
<!-- Def elements -->
<template bind="{{item}}" ref="itemTemplate"></template> <!-- this is the replacement of content tag -->
<!-- Def elements -->
</template>
</template>
<script type="application/dart" src="data-ul.dart"></script>
</polymer-element>
then you should have next class:
#CustomTag('data-ul')
class DataUl extends LiElement with Polymer, Observable {
DataUl.created() : super.created();
#published List items;
void parseDeclaration(Element elementElement) {
// We need to remove previous template from element.templateContent
// in that way it no continues adding a new content every time that we instantiate
// this component.
var previousTemplate = element.templateContent.querySelector('template#item');
if(previousTemplate != null)
previousTemplate.remove();
var t = this.querySelector('#itemTemplate'); // Gets the template with id itemTemplate from the content html
if(t != null) // if not null
element.templateContent.append(t); // append itemTemplate to element.templateContent
else
element.templateContent.append(new TemplateElement()..id='itemTemplate'); //if no template is added append an empty template to avoid errors
super.parseDeclaration(elementElement); // call super
}
}
And finally use the custom element as follow:
<ul is="data-ul" items="{{[{'first_name': 'jay'}, {'first_name': 'joy'}]}}">
<template id="itemTemplate">
<li>{{item['first_name']}}</li>
</template>
</ul>

How do I get jQuery Mobile to re-execute on generated code?

I'm trying to generate some jQuery Mobile elements with Javascript. After the javascript runs and places the generated elements in the myTest div, the styling and scripting are not attached as they are on the static content. Is there any way to have jQuery execute on the generated code?
Here's an example:
Markup:
<!-- Does not look correct when populated -->
<div id="myTest">
</div>
<!-- Looks correct -->
<div data-role="collapsible-set" data-theme="d" data-content-theme="d" data-mini="true" data-corners="false">
<div data-role="collapsible">
<h3>Test</h3>
</div>
</div>
Script:
$(document).ready(onloadFunc);
function onloadFunc() {
var parent = $('<div data-role="collapsible-set" data-theme="d" data-content-theme="d" data-mini="true" data-corners="false">');
var item = $("<h3>").html("test");
parent.append(item);
$("#myTest").append(parent);
}
Link to jsfiddle: http://jsfiddle.net/DcFhj/
First off your markup is actually slightly off, the collapsible-set widget is meant to contain within it several collapsible widgets.
For example (taken from the documentation)
<div data-role="collapsible-set">
<div data-role="collapsible" data-collapsed="false">
<h3>Section 1</h3>
<p>I'm the collapsible set content for section 1.</p>
</div>
<div data-role="collapsible">
<h3>Section 2</h3>
<p>I'm the collapsible set content for section 2.</p>
</div>
</div>
Have a look at the following question from the jQuery Mobile documentation. Basically in general in order to enhance markup that is dynamically inserted you have to either initialize the widget on the markup or you can trigger the create event on a parent element and jQuery Mobile should initialize all of the appropriate widgets.
In this case being that you just have one widget you just need to initialize the collasible widget (also interesting enough this works with your current markup)
$("#myTest").append(parent).find('div').collapsible();
http://jsfiddle.net/DcFhj/3/
If for example you had several widgets that needed enhancement (or just for simplicity) you could instead trigger the create method (this doesn't work with your current markup but if you correct it it should).
$("#myTest").append(parent).trigger('create');
http://jsfiddle.net/DcFhj/4/

Custom Element - null reference from query() in constructor

I'm working on my first Dart app, having completed the Game of Darts tutorials. I am trying to create a semantically named top-menu element that will eventually display a list of navigation menu tabs at the top of my page. My Dart app is able to recognize my custom element and calls the associated constructor.
However, I am getting a null reference when trying to query for the UL element within my custom element. I need the UL reference in order to dynamically load my LI elements into the menu.
Question 1:
Should the element be visible in the DOM at the point where the constructor is running?
Question 2:
If it is not yet visible, is there a Dart event I can use to trigger loading of the LI elements after the custom element has been completely loaded into the DOM?
Thanks in advance! For reference, here is the source of my custom element:
topmenu-element.html
<!DOCTYPE html>
<html>
<body>
<element name="top-menu" constructor="TopMenu" extends="div">
<template>
<div>
Top Menu
<ul id="top-menu-list"></ul>
</div>
</template>
<script type="application/dart" src="topmenu-element.dart"></script>
</element>
</body>
</html>
topmenu-element.dart
import 'package:web_ui/web_ui.dart';
import 'dart:html';
class TopMenu extends WebComponent {
List menuItems = ['Session', 'Authentication Services', 'Vehicle Services', 'Subscriber Services', 'Data Services'];
void populateMenu() {
UListElement menuList = query('#top-menu-list');
LIElement newMenuItem = new LIElement();
newMenuItem.text = menuItems[0];
menuList.children.add(newMenuItem);
}
TopMenu() {
// populateMenu();
}
}
I can't speak specifically about the DOM visibility in a constructor with the query method as I'm truthfully not certain. However there are perhaps better methods which you can use, which are called at various stages in the elements lifecycle.
That said, can I ask why you need to use this particular method to add the children. It is probably much easier to do it with the template repeat like so:
<!DOCTYPE html>
<html>
<body>
<element name="top-menu" constructor="TopMenu" extends="div">
<template>
<div>
Top Menu
<ul id="top-menu-list">
<li template repeat="item in menuItems">{{item}}</li>
</ul>
</div>
</template>
<script type="application/dart" src="topmenu-element.dart"></script>
</element>
</body>
</html>
Then there's no need to put any of your menu display code in your constructor.

Is it possible to add dynamic jQuery tabs using KnockoutJS Templates?

I'm trying to build a dynamic display in a web page. The site is built in ASP.NET MVC 3, but I'm not sure if this matters...
Essentially, I have a base "_Editor.cshtml" page which uses partials to render the specific editor content in my display using query parameters. Consider an editor for a chart (line chart vs. bar chart).
_Editor.cshtml
<div id='tabs'>
<ul>
<li><a href='#queryTab'>Define Query</a></li>
<!-- THIS IS WHERE I NEED TO SHOW ALL OF MY 'CUSTOM' TAB NAMES -->
<!-- ko template: 'tabNames' -->
<!-- /ko -->
<li><a href='#themeTab'>Styles and Themes</a></li>
</ul>
<div id='queryTab'>
<!-- configure db connection, tables, fields, etc... (not important) -->
</div>
<!-- THIS IS WHERE I WANT TO SHOW CONTENT FOR MY CUSTOM TABS -->
<!-- ko template: 'tabContent' -->
<!-- /ko -->
<div id='themeTab'>
<!-- show various style options here (not important) -->
</div>
</div>
<script type="text/javascript">
$(function(){
var vm = { /* define my model here */ };
ko.applyBindings(vm);
$("#tabs").tabs();
});
</script>
#if (Model.EditorType == ChartTypes.Bar) {
#Html.Partial("_BarChartEditor")
} else if (Model.EditorType == ChartTypes.Line) {
#Html.Partial("_LineChartEditor")
}
_BarChartEditor.cshtml
<script id="tabNames" type="text/html">
<li>Bar Chart Tab!</li>
<!-- I could have many more tab names here -->
</script>
<script id="tabContent" type="text/html">
<div id="barChartTab">
<h1>Add controls for bar chart configuration</h1>
</div>
<!-- I would create a div for each tab name above here -->
</script>
_LineChartEditor.cshtml
<script id="tabNames" type="text/html">
<li>Line Chart Tab!</li>
</script>
<script id="tabContent" type="text/html">
<div id="lineChartTab">
<h1>Add controls for line chart configurations</h1>
</div>
</script>
Sorry for the lengthy code drop (it took a long time to write it all here, so have mercy on me). :) I wanted to make sure that my problem was understood because of the way I'm building my editors using custom partials. Perhaps it's cludgy, but it works for the most part...
Really, everything works great except for the tab content. The tab rendering appears to be happening before I'm able to bind my view model to the UI. When the 'ko.applyBindings()' method is called, the tab shows up on different tabs.
Has anybody tried to do this? Has anybody had success? I've created a jsfiddle to show a simple scenario to show exactly what I'm talking about:
http://jsfiddle.net/TrailHacker/j2nhm/
Thanks for any help!
I actually got it working with your example, your content template was just structured incorrectly. It was missing the <div> tags.
If you modify this for your example, just remember that the div id needs to match the link's ref. You can throw both of these values into your viewmodel, to allow for multiple custom tabs.
http://jsfiddle.net/tyrsius/UCGRZ/

Resources