I try to create a vaadin component with polymer. I have used vaadin/flow-spring-tutorial as starting point. But I don't understand how to edit a template.
The template looks like this:
<!-- Defines the example-template element -->
<dom-module id="example-template">
<template>
<span>[[message]]</span>
</template>
<!-- Polymer boilerplate to register the example-template element -->
<script>
class ExampleTemplate extends Polymer.Element {
static get is() {
return 'example-template'
}
}
customElements.define(ExampleTemplate.is, ExampleTemplate);
</script>
</dom-module>
When I edit something. E.g.
<span>[[message]]!!TEST</span
It doesn't has any effect after restarting the embedded webserver.
The output is still
Hello from bean org.vaadin.spring.tutorial.Greeter#xxxxx
There is no directory that contains a compiled or cached version of this template and I don't know what I have to do to make the change take effect.
Related
I am using beta3 of Vaadin 10 and I have a html-file bound to a Component (#HtmlImport), which contains a <dom-repeat> inside of which I have a paper-radio-group. I want the paper-radio-group#selected-property to be bound two way to my model, so that when a user selects a different radio-button, it's value will be written back to the model. Unfortunately, for me it works only as a one way model, as the java-side setter setAOrBProperty() is never called. Can someone give me a hint what I need to do to have the new value written to the server?
<link rel="import" href="./bower_components/polymer/polymer-element.html">
<link rel="import" href="bower_components/paper-radio-button/paper-radio-button.html">
<link rel="import" href="bower_components/paper-radio-group/paper-radio-group.html">
<dom-module id=“dmmdl”>
<template>
<div>
<dom-repeat items=“[[myListOfSomething]]”>
<div>
<paper-radio-group selected="{{item.aOrBProperty}}” allow-empty-selection>
<paper-radio-button name=“a”>A</paper-radio-button>
<paper-radio-button name=“b”>B</paper-radio-button>
</paper-radio-group>
</div>
</template>
</div>
</template>
<script>
class BooksGridElement extends Polymer.Element {
static get is() {
return 'books-grid'
}
// only for testing !!
// ready() {
// super.ready();
// this.books = results;
// }
}
customElements.define(BooksGridElement.is, BooksGridElement);
</script>
</dom-module>
I suspect this is caused by a security feature of Flow. Arbitrary model value changes from the client are not accepted for security reasons. Instead, changes are only allowed for properties that are used in two-way template bindings (i.e. {{propertyName}}) or explicitly annotated with #AllowClientUpdates on the corresponding Java getter.
The logic that looks for {{propertyName}} doesn't have any specific knowledge about the inner workings of <dom-repeat>, so it cannot know that {{item.aOrBProperty}} corresponds to myListOfSomething[*].aOrBProperty.
If my assumption is correct, you could fix this case by adding #AllowClientUpdates to the getAOrBProperty() method in your model interface.
I am at: https://elements.polymer-project.org/elements/paper-dialog-behavior?active=Polymer.PaperDialogBehavior
and decided to create a dialog by doing something like:
<dom-module id="sample-dialog">
<template>
<paper-dialog-impl>
<h2>{{title}}</h2>
<div>
<content></content>
</div>
<div class="buttons">
<paper-button dialog-dismiss on-tap="cancel">Cancel</paper-button>
<paper-button dialog-confirm on-tap="submit">Accept</paper-button>
</div>
</paper-dialog-impl>
</template>
</dom-module>
it renders to the screen, but has no styles. I read: paper-dialog-shared-styles.html provide styles for a header, content area, and an action area for buttons but i don't know how to implement it. I was trying to use a link tag but that wasn't working. Specifically I tried inside the template: <link rel="import" href="polymer_elements/src/paper-dialog-behavior/paper-dialog-shared-styles.html" />
In my dart file, i imported these too, thinking it woudl be resolved.
import 'package:polymer_elements/paper_button.dart';
import 'package:polymer_elements/paper_dialog_behavior.dart';
Edit: Right now, I have the class Implementation extending Polymer Element, which makes sense. I just wasnt sure if it needed to extend something else as well.
Edit 2: Updated this to be a standard paper-dialog instead, except now it wont render the item at all, even after updating the import in the dart backend.
After updating it to a paper dialog, there is a property that needs to be applied to the paper-dialog for it to be visible or not. that is the opened attribute.
Adding that to the inner paper-dialog makes it visible.
So, therefore, passing it down to the paper dialog by way of an outer opened, will make it toggle open/closed.
//in the sample-dialog dart
#property bool opened = false;
in the markup:
<paper-dialog opened$="{{opened}}" ...>
then now i can say either:
<sample-dialog></sample-dialog>
<sample-dialog opened></sample-dialog>
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>
Short Version: The custom web component example in the first link isn't working for me. Why not? I'm running this in Dartium.
Long Version:
I copied and pasted this Dart Web UI example from this tutorial into the Dart Editor and tried to run it. Nothing showed up on the page. I've used dart before but not with web components, so I noticed that one difference between this code and other code I've written is that there was no <script src="packages/browser/dart.js"></script>, so I added that at the bottom. Now I got the error:
Internal error: Dart_Invoke: did not find top-level function 'main'.
I put a print statement in the main() function, and the text was printed before the error, which is strange. I guessed and tried adding main() {} inside the <script> tag that was in the custom component. That is, the script tag looked like:
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
main() {print("in main in component");}
class CounterComponent extends WebComponent {
int count = 0;
void increment() { count++; }
}
</script>
Now that error goes away, and both print statements are printed, but nothing happens.
Here is the original tutorial code for your convenience:
<!DOCTYPE html>
<!--
Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
for details. All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.css">
</head>
<body>
<element name="click-counter" constructor="CounterComponent" extends="div">
<template>
<button on-click="increment()">Click me</button>
<span>(click count: {{count}})</span>
</template>
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
class CounterComponent extends WebComponent {
int count = 0;
void increment() { count++; }
}
</script>
</element>
<div class="well">
<div is="click-counter"></div>
</div>
<script type="application/dart">
main(){}
</script>
</body>
</html>
Web UI applications need to be "built" (usually by a script called build.dart in the project root, see this article for more details).
If I go to the Dart Editor, create a new test project using the wizard and selecting the Web Project (using the web_ui library) option, this creates the boilerplate including the build script.
Open up the html file and paste in the code from the github tutorial, replacing what is there already. When you save the file, the build.dart will be invoked, outputting the build version to /web/out/
Hit the run button, and the Dart Editor will open the app in Dartium (it knows to add /out/ to the URL).
I am going to build complex application having many different views. Imagine for example eshop solution. There can be a lot of different views:
Contact page with some static information
Registration form for new customers
View of your order
List of the products
Details about a product
...
Now I am little bit confused, how to build such a complex application using Web UI. I would like to have the HTML templates for the views separated in multiple files and having some logic to determine, which one should be rendered.
Assume I want to have a single master template containing basic things like header and footer, then I have a lot of content templates, these should get injected into the right place inside the master template.
Until now, I have always seen only small single-template examples of using Dart Web UI, so I have no idea, how to achieve this.
I've put together a little example of how I currently do it (hope we will soon see a larger best practice example application for this):
For the complete source code of this example see gist: How to build a Web UI application with multiple views in Dart
Main Application
app.html - Contains the main application layout, instantiates the header and footer component and creates a container for the views.
app.dart - Handles navigation events and replaces the view inside the view container (see below)
app.css
Web Components
Header and Footer
header.html - Web Component for header
footer.html - Web Component for footer
Views
contact.html - Web Component for the Contacts View
contact.dart - Dart file containing ContactsView class
products.html - Web Component for the Products View
products.dart - Dart file containing ProductsView class
Switching Between Views
The standard way to instantiate Web Components is by using <x-foo></x-foo> in HTML.
As we have different views, we will have to instantiate the Web Components inside our Dart code. Doing this we have to manually call the Web Components lifecycle methods. This is not straight forward and might be improved in the future (see Issue 93 which also contains some exmples).
Here is how you can switch views (source of app.dart):
import 'dart:html';
import 'package:web_ui/web_ui.dart';
import 'contact.dart';
import 'products.dart';
void main() {
// Add view navigation event handlers
query('#show-contact-button').onClick.listen(showContactView);
query('#show-products-button').onClick.listen(showProductView);
}
// Used to call lifecycle methods on the current view
ComponentItem lifecycleCaller;
/// Switches to contacts view
void showContactView(Event e) {
removeCurrentView();
ContactView contactView = new ContactView()
..host = new Element.html('<contact-view></contact-view>');
lifecycleCaller = new ComponentItem(contactView)..create();
query('#view-container').children.add(contactView.host);
lifecycleCaller.insert();
}
/// Switches to products view
void showProductView(Event e) {
removeCurrentView();
ProductsView productsView = new ProductsView()
..host = new Element.html('<products-view></products-view>');
lifecycleCaller = new ComponentItem(productsView);
lifecycleCaller.create();
query('#view-container').children.add(productsView.host);
lifecycleCaller.insert();
}
void removeCurrentView() {
query('#view-container').children.clear();
if (lifecycleCaller != null) {
// Call the lifecycle method in case the component needs to do some clean up
lifecycleCaller.remove();
}
}
And here is the source for app.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A Complex Web UI Application</title>
<link rel="stylesheet" href="app.css">
<!-- import the header and footer components -->
<link rel="components" href="header.html">
<link rel="components" href="footer.html">
<!-- import the view components -->
<link rel="components" href="contact.html">
<link rel="components" href="products.html">
</head>
<body>
<header-component></header-component>
<div id="view-container"></div>
<button id="show-contact-button">show contact view</button>
<button id="show-products-button">show products view</button>
<footer-component></footer-component>
<script type="application/dart" src="app.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
Note: I had to import the view components via <link rel="components" href="contact.html"> even though I do not directly reference it in the HTML file.
You can use the route library combined with templates to greatly automate the process.
In urls.dart you will define the routes that the app will handle. app.dart will setup the route listener. Lastly, app.html will hold a page container that will automatically switch the page component (through the use of template instantiation).
With this structure set up, page navigation can be handled through regular anchor tags instead of calling custom functions to change the page.
In order to add a new page you will have to do the following:
Add a new route in urls.dart
Create a new WebComponent in the pages/ folder
Add a new conditional template for the page in app.html
Below you can see an example of an app that handles a home page and a contact page:
urls.dart:
library urls;
import 'package:route/url_pattern.dart';
final homeUrl = new UrlPattern(r'/');
final contactUrl = new UrlPattern(r'/contact');
app.dart:
import 'dart:html';
import 'package:web_ui/web_ui.dart';
import 'package:route/client.dart';
import 'urls.dart' as urls;
import 'package:web_ui/watcher.dart' as watchers;
// Setup the routes to listen to
void main() {
var router = new Router()
..addHandler(urls.homeUrl, showPage)
..addHandler(urls.contactUrl, showPage)
..listen();
}
// Change the page that we are on
void showPage(String path) {
watchers.dispatch();
}
app.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample app</title>
<link rel="stylesheet" href="app.css">
<!-- import the pages -->
<link rel="components" href="pages/xhomepage.html">
<link rel="components" href="pages/xcontactpage.html">
</head>
<body>
<!-- You could put a header here if you want !-->
<!-- Templates take care of automatically switching the page !-->
<div class="pages">
<template instantiate="if urls.homeUrl.matches(window.location.pathname)">
<x-home-page></x-home-page>
</template>
<template instantiate="if urls.contactUrl.matches(window.location.pathname)">
<x-contact-page></x-contact-page>
</template>
</div>
<!-- You could put a footer here if you want !-->
<script type="application/dart" src="app.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
Edit: I've removed the step where app.dart has to define its own pages. Instead, templates check to see if the URL path matches the UrlPattern defined in urls.dart. This should simplify things a bit more.
I created a Polymer element <bind-view> that creates and adds a view element depending on the current route. The element works with the route_hierarchical package.
See BWU Polymer Routing on GitHub for more details.
A route configuration looks like
library bwu_polymer_routing_example.route_initializer;
import 'package:route_hierarchical/client.dart' as rt;
import 'package:bwu_polymer_routing/module.dart';
class RouteInitializer implements Function {
void call(rt.Router router, RouteViewFactory views) {
views.configure({
'usersList': routeCfg(
path: '/users',
view: 'user-list',
defaultRoute: true,
dontLeaveOnParamChanges: true,
enter: (route) => router.go('usersList', {})),
'user': routeCfg(
path: '/user/:userId',
view: 'user-element',
dontLeaveOnParamChanges: true,
mount: {
'articleList': routeCfg(
path: '/articles',
view: 'article-list',
defaultRoute: true,
dontLeaveOnParamChanges: true,
mount: {
'article': routeCfg(
path: '/article/:articleId',
view: 'article-element',
bindParameters: ['articleId', 'userId'],
dontLeaveOnParamChanges: true,
mount: {
'view': routeCfg(
path: '/view',
defaultRoute: true,
dontLeaveOnParamChanges: true),
'edit': routeCfg(
path: '/edit',
dontLeaveOnParamChanges: true)
})
})
})
});
}
}
the <app-element> contains the <bind-view> element, a placeholder where the view configured for the current route gets added.
Views can be nested. Any view can itself contain a <bind-view> element. This allows to create hierarchical view composition without much boilerplate.
<!DOCTYPE html>
<link rel='import' href='../../../../packages/polymer/polymer.html'>
<link rel='import' href='../../../../packages/bwu_polymer_routing/bind_view.html'>
<link rel='import' href='user_list.html'>
<link rel='import' href='user_element.html'>
<link rel='import' href='article_list.html'>
<link rel='import' href='article_element.html'>
<polymer-element name='app-element'>
<template>
<bind-view id='app-element'></bind-view>
</template>
<script type='application/dart' src='app_element.dart'></script>
</polymer-element>
The app_element.dart file contains the router initialization code
class AppModule extends Module {
AppModule() : super() {
install(new RoutingModule(usePushState: true));
bindByKey(ROUTE_INITIALIZER_FN_KEY, toValue: new RouteInitializer());
}
}
#CustomTag('app-element')
class AppElement extends PolymerElement with DiContext {
AppElement.created() : super.created();
#override
void attached() {
super.attached();
initDiContext(this, new ModuleInjector([new AppModule()]));
}
}
The package also contains some helper mixins to add dependency injection (DI) functionality to Polymer elements like the DiContext mixin used here.
Constructor injection can't be used with Polymer but events are a good substitute.
The DiConsumer mixin allows to request an instance from DI with this simple code
#CustomTag('article-list')
class ArticleList extends PolymerElement with DiConsumer {
#observable String userId;
#override
void attached() {
super.attached();
// The two lines below show how to request instances from DI
// but they are redundant here because
// route parameters are assigned to attributes of the view automatically
// when the view is created or when the values change
var di = inject(this, [RouteProvider /* add more types here as needed */]);
userId = (di[RouteProvider] as RouteProvider).parameters['userId'];
}
}