How to trigger a function inside NgComponent from outside of the component? - dart

I have the following component
#NgComponent(selector: 'foo',
template: '<div>foo component</div>')
class FooComponent {
void doSomething();
}
it's used as follows:
<html>
<head></head>
<body>
<foo ng-click="ctrl.doSomething()"></foo> // This is wrong
</body>
</html>
How do I actually execute a function inside an NgComponent?

Good question
What I come up with (probably not exactly what you are looking for):
#NgController(
selector: '[do-something]',
publishAs: 'ctrl'
)
class DoSomething {
FooComponent _foo;
DoSomething(this._foo);
void clickHandler(e) {
_foo.doSomething();
}
}
.
<foo do-something ng-click="ctrl.doSomething()"></foo>

Here is a one poor solution, but if there is no other solution, then you can use this.
EDIT: I updated this solution completely. With this example one can define what event component recognizes and to what function each event is attached.
html:
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<title>Foo</title>
<link rel="stylesheet" href="ok_comp.css">
</head>
<body>
<foo click="test()" doubleclick="test2()"></foo>
<foo click="test2()"></foo>
<script type="application/dart" src="ok_comp.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
comp.dart:
import 'dart:html';
import 'package:angular/angular.dart';
#NgComponent(
selector: 'foo',
template: '<div>foo</div>'
)
class FooComp extends NgAttachAware {
#NgAttr('click')
var click;
#NgAttr('doubleclick')
var doubleclick;
Element element;
var func;
FooComp(this.element){
}
attach(){
attachFunc("click", click);
attachFunc("doubleclick", doubleclick);
}
void attachFunc(String listener, String funcName){
switch (funcName) {
case 'test()':
func = test;
break;
case 'test2()':
func = test2;
break;
}
switch (listener) {
case 'click':
element.onClick.listen(func);
break;
case 'doubleclick':
element.onDoubleClick.listen(func);
break;
}
}
test(MouseEvent event){
print ("test");
}
test2(MouseEvent event){
print ("test2");
}
}
class MyAppModule extends Module {
MyAppModule() {
type(FooComp);
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}

You can add an event listener to component. Here is an example:
html:
<foo></foo>
comp.dart:
#NgComponent(selector: 'foo',
template: '<div>foo component</div>')
class FooComponent {
FooComponent(Element elem){
elem.onClick.listen(doSomething);
}
void doSomething(MouseEvent event){
print("click");
}
}

This question keeps bothering me and I had to test it a little bit more. In the following example a component has multiple functions and multiple build-in ng-directives. You can define which functions are related to which ng-directives through component's attributes.
html:
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<title>Foo</title>
<link rel="stylesheet" href="ok_comp.css">
</head>
<body>
<foo2 click="test" doubleclick="test2"></foo2>
<foo2 click="test2"></foo2>
<script type="application/dart" src="ok_comp.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
comp.dart:
import 'dart:html';
import 'package:angular/angular.dart';
#NgComponent(
selector: 'foo2',
template: '<div ng-click="cmp.ngClick()" ng-doubleclick="cmp.ngDoubleClick()">foo2</div>',
publishAs: 'cmp'
)
class Foo2Comp extends NgAttachAware {
#NgAttr('click')
var strClick;
#NgAttr('doubleclick')
var strDoubleclick;
var ngClick;
var ngDoubleClick;
Foo2Comp(){
}
attach(){
ngClick = redirectFunc(strClick);
ngDoubleClick = redirectFunc(strDoubleclick);
}
redirectFunc(String funcName){
var ng;
switch (funcName) {
case 'test':
ng = test;
break;
case 'test2':
ng = test2;
break;
default:
ng = empty;
break;
}
return ng;
}
empty(){
print ("empty");
}
test(){
print ("test");
}
test2(){
print ("test2");
}
}
class MyAppModule extends Module {
MyAppModule() {
type(Foo2Comp);
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}

Related

grails javax.websocket issues

Grails 2.3.7 - Java 1.7
I have seen the following example used in core java and working as a demo, trying to achieve the same in Grails, I know there are a few plugins around websockets but I was trying to figure this out on my own :
Controller 1
package chat
class TestController {
def index() { }
}
index.gsp
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'admin.label', default: 'Admin')}" />
<title><g:message code="default.create.label" args="[entityName,BAH,BAH]" /></title>
</head>
<body>
<form>
<input id="textMessage" type="text">
<input type="button" value="send" onClick="sendMessage();">
</form>
<br>
<textarea id="messagesTextarea" rows="10" cols="50">
</textarea>
<script type="text/javascript">
var webSocket=new WebSocket("ws://localhost:8080/chat/testing");
var messagesTextarea=document.getElementById("messagesTextarea");
var textMessage=document.getElementById("textMessage");
webSocket.onopen=function(message) {processOpen(message);};
webSocket.onmessage=function(message) {processMessage(message);};
webSocket.onclose=function(message) {processClose(message);};
webSocket.onerror=function(message) {processError(message);};
function processOpen(message) {
messagesTextarea.value +=" Server Connect.... "+"\n";
}
function processMessage(message) {
messagesTextarea.value +=" Receive from Server ===> "+ message.data +"\n";
}
function sendMessage() {
if (textMssage.value!="close") {
webSocket.send(textMessage.value);
messagesTextarea.value +=" Send to Server ===> "+ textMessage.value +"\n";
textMessage.value="";
}else {
websocket.close();
}
}
function processClose(message) {
webSocket.send("Client disconnected......");
messagesTextarea.value +="Server Disconnected... "+"\n";
}
function processError(message) {
messagesTextarea.value +=" Error.... \n";
}
</script>
</body>
</html>
Controller 2:
package chat
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/testing")
class TestingController {
#OnOpen
public void handleOpen() {
System.out.println("Client is now connected.");
}
#OnMessage
public String handleMessage(String message) {
System.out.println("Client sent: " + message);
String replyMessage = "echo "+message;
System.out.println("Send to Client: " + replyMessage);
return replyMessage;
}
#OnClose
public void handeClose() {
System.out.println("Client is now disconnected.");
}
#OnError
public void handleError(Throwable t) {
t.printStackTrace();
}
}
With this as is when I run app
I get the following error in chrome:
WebSocket connection to 'ws://localhost:8080/chat/testing' failed: Error during WebSocket handshake: Unexpected response code: 404 index:37
WebSocket is already in CLOSING or CLOSED state.
and in textArea
Error....
Server Disconnected...
on ggts console I see:
Client sent: null
Send to Client: echo null
Initially I attempted controller this way:
package chat
class TestingController extends TestingEndpoint {
}
and in src/java/chat
package chat;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/testing")
class TestingEndpoint {
#OnOpen
public void handleOpen() {
System.out.println("Client is now connected.");
}
#OnMessage
public String handleMessage(String message) {
System.out.println("Client sent: " + message);
String replyMessage = "echo "+message;
System.out.println("Send to Client: " + replyMessage);
return replyMessage;
}
#OnClose
public void handeClose() {
System.out.println("Client is now disconnected.");
}
#OnError
public void handleError(Throwable t) {
t.printStackTrace();
}
}
This method produced same result except nothing in ggts console
Wondering if anyone has got javax.websocket to work in Grails..
ok got it working - was not that bad after all
here is the fix:
a few typos in the gsp :
index.gsp
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'admin.label', default: 'Admin')}" />
<title><g:message code="default.create.label" args="[entityName,BAH,BAH]" /></title>
</head>
<body>
<form>
<input id="textMessage" type="text">
<input type="button" value="send" onClick="sendMessage();">
</form>
<br>
<textarea id="messagesTextarea" rows="10" cols="50">
</textarea>
<script type="text/javascript">
var webSocket=new WebSocket("ws://localhost:8080/chat/annotated");
var messagesTextarea=document.getElementById("messagesTextarea");
webSocket.onopen=function(message) {processOpen(message);};
webSocket.onmessage=function(message) {processMessage(message);};
webSocket.onclose=function(message) {processClose(message);};
webSocket.onerror=function(message) {processError(message);};
function processOpen(message) {
messagesTextarea.value +=" Server Connect.... "+"\n";
}
function processMessage(message) {
messagesTextarea.value +=" Receive from Server ===> "+ message.data +"\n";
}
function sendMessage() {
if (textMessage.value!="close") {
webSocket.send(textMessage.value);
messagesTextarea.value +=" Send to Server ===> "+ textMessage.value +"\n";
textMessage.value="";
}else {
websocket.close();
}
}
function processClose(message) {
webSocket.send("Client disconnected......");
messagesTextarea.value +="Server Disconnected... "+"\n";
}
function processError(message) {
messagesTextarea.value +=" Error.... \n";
}
</script>
</body>
</html>
Now the actual fix for the end point, I stumbled across it from here:
https://tyrus.java.net/documentation/1.7/index/deployment.html
Example 3.2. Deployment of Annotated Endpoint Using ServerContainer
So the fix was to add a src/java/MyServletContextListenerAnnotated.java
package chat;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
#WebListener
#ServerEndpoint("/annotated")
public class MyServletContextListenerAnnotated implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
final ServerContainer serverContainer = (ServerContainer) servletContextEvent.getServletContext()
.getAttribute("javax.websocket.server.ServerContainer");
try {
serverContainer.addEndpoint(MyServletContextListenerAnnotated.class);
} catch (DeploymentException e) {
e.printStackTrace();
}
}
/* #OnMessage
public String onMessage(String message) {
return message;
}
*/
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
#OnOpen
public void handleOpen() {
System.out.println("Client is now connected.");
}
#OnMessage
public String handleMessage(String message) {
System.out.println("Client sent: " + message);
String replyMessage = "echo "+message;
System.out.println("Send to Client: " + replyMessage);
return replyMessage;
}
#OnClose
public void handeClose() {
System.out.println("Client is now disconnected.");
}
#OnError
public void handleError(Throwable t) {
t.printStackTrace();
}
}
Since the endpoint in gsp already updated to use new endpoint the final touch was to add _Events.groovy to scripts:
import groovy.xml.StreamingMarkupBuilder
eventWebXmlEnd = {String tmpfile ->
def root = new XmlSlurper().parse(webXmlFile)
root.appendNode {
'listener' {
'listener-class' (
'chat.MyServletContextListenerAnnotated'
)
}
}
webXmlFile.text = new StreamingMarkupBuilder().bind {
mkp.declareNamespace(
"": "http://java.sun.com/xml/ns/javaee")
mkp.yield(root)
}
}
and booom there it is - server connected client send blah

Display a simple AngularDart component

I'am starting to learn Dart/AngularDart and i'am trying to display a simple component following the tutorial in https://angulardart.org/ , my problem is that i got a blank page nothing is displayed.
Here is my code:
web/nasiha.dart
import 'dart:html';
import 'package:angular/angular.dart';
import 'components/post/post.dart';
import 'dart:mirrors';
class MyAppModule extends Module {
MyAppModule() {
type(PostComponent);
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}
web/nasiha.html
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<title>Nasiha</title>
<link rel="stylesheet" href="css/nasiha.css">
</head>
<body>
<post></post>
<script src="packages/shadow_dom/shadow_dom.min.js"></script>
<script type="application/dart" src="nasiha.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
web/components/post/post.dart
import 'package:angular/angular.dart';
#NgComponent(
selector: 'post',
templateUrl:'components/post/post.html',
cssUrl: 'components/post/post.css',
publishAs: 'cmp_post'
)
class PostComponent {
String text= "This is a simple text to show";
String userName = "test";
DateTime date= new DateTime.now();
PostComponent(String text, String userName, DateTime date){
this.text = text;
this.userName = userName;
this.date = date;
}
String getText(){
return this.text;
}
void setText(String text){
this.text = text;
}
DateTime getDate(){
return this.date;
}
void setDate(DateTime date){
this.date = date;
}
String getUserName(){
return this.userName;
}
void setUserName(String userName){
this.userName = userName;
}
}
web/components/post/post.html
<div>
<p ng-model="cmp_post.post_text">
{{cmp_post.text}}
</p>
<div ng-model="cmp_post.post_date">
{{cmp_post.date}}
</div>
<div ng-model="cmp_post.post_username">
{{cmp_post.userName}}
</div>
</div>
You should execute pub upgrade from the context menu on pubspec.yaml.
The ng-model attributes in web/components/post/post.html are redundant.
PostComponent(String text, String userName, DateTime date){
this code is invalid.
Either you register a class in your module that can be injected to
the constructor or you use annotations to be able to inject primitive
types like String, int, double, ... (If you want to know how inject primitive types or using annotations for injection see How can I Dependency Inject based on type and name, with AngularDart?

Repeat over elements provided as content

When I create a component in Angular.dart like
library main;
import 'package:angular/angular.dart';
import 'package:di/di.dart';
class Item {
String name;
Item(this.name);
}
#NgComponent(
selector: 'my-component',
publishAs: 'ctrl',
applyAuthorStyles: true,
template: '''<div ng-repeat="value in ctrl.values"><span>{{value.name}}</span> - <content><content></div>'''
)
class MyComponent {
List<Item> values = [new Item('1'), new Item('2'), new Item('3'), new Item('4')];
MyComponent() {
print('MyComponent');
}
}
class MyAppModule extends Module {
MyAppModule() {
type(MyComponent);
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}
and use it like
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
</head>
<body>
<h3>Repeat</h3>
<my-component>
<div>some provided content to repeat</div>
</my-component>
<script type="application/dart" src="index.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
I get
I know the <content> tag isn't working that way in web components.
But is there any other way, some manipulation I can do in my component, to get the <div> provided as child element repeated?
I solved it like
Code of <my-component>
#NgComponent(
selector: 'my-component',
publishAs: 'ctrl',
template: '''<div ng-repeat="value in ctrl.values"><span ng-bind-nodes="ctrl.nodes"></span><span>something hardcoded: {{value.name}}</span></div><content id='content'></content>'''
)
class MyComponent extends NgShadowRootAware {
List<Item> values = [new Item('1'), new Item('2'), new Item('3'), new Item('4')];
List<dom.Node> nodes = new List<dom.Node>();
MyComponent();
#override
void onShadowRoot(dom.ShadowRoot shadowRoot) {
nodes.addAll((shadowRoot.querySelector('#content') as dom.ContentElement).getDistributedNodes());
//nodes.forEach((n) => print(n));
nodes.forEach((n) => n.remove());
}
}
The component removes it's child nodes and provides them in the field nodes
the directive ng-bind-nodes
adds the nodes to the element where it is applied
#NgDirective(
selector: '[ng-bind-nodes]',
publishAs: 'ctrlx' // conflicts with my-component
)
class NgBindNodesDirective {
dom.Element _element;
MyComponent _myComponent;
Scope _scope;
Compiler _compile;
Injector _injector;
NgBindNodesDirective(this._element, this._myComponent, this._scope, this._compile, this._injector);
#NgOneWay('ng-bind-nodes') set nodes(var nodes) {
print(nodes);
if(nodes == null) {
return;
}
_element.nodes.clear();
nodes.forEach((dom.Node node) {
_element.nodes.add(node.clone(true));
});
BlockFactory template = _compile(_element.nodes);
Block block = template(_injector, _element.nodes);
}
}
I don't have an answer, and I can't test my suggestion right now, but try injecting the element, compiler, scope and blockfactory in MyComponent:
Element element;
Compiler compiler;
Injector injector;
Scope scope;
MyComponent(this.element, this.compiler, this.injector, this.scope) {
}
You can access the div as child of 'element'.
Then you don't use template of NgComponent, but instead build your own template from a string, insert the child and compile it:
String template = '''<div ng-repeat="value in ctrl.values"><span>{{value.name}}</span> - <div id="inner"><div></div>''';
void onShadowRoot(ShadowRoot shadowRoot) {
List<DivElement> children = element.children;
shadowRoot.appendHtml(template);
DivElement inner = shadowRoot.querySelector('#inner');
inner.children.add(children);
BlockFactory fact = compiler([shadowRoot]);
Scope childScope = scope.$new();
Injector childInjector =
injector.createChild([new Module()
..value(Scope, childScope)]);
fact(childInjector, children);
}
Maybe it gives you the right direction.

Dart: Observable variable not updating

I have a Dart app in which I need to set a number of fields based on what's coming back from a JSON request. I'm extending PolymerElement and using #observable to keep the field in sync. What I want to be able to do is to set a global variable and have that drive the class level variable. I'm sure I'm missing something fundamental, but I can't access the object that's being used by the HTML because it hasn't been instantiated (or has it?).
mobilemenu.dart
library mobilemenu;
import 'dart:html';
import 'order_item.dart' as orderitem;
main() {}
mobilemenu.html
<!DOCTYPE html>
<html>
<head>
<link rel="import" href="order_item.html">
<script src="packages/polymer/boot.js"></script>
</head>
<body>
<div class="mobile-menu-product-name">
<order-item></order-item>
</div>
</body>
</html>
order_item.dart
library orderitem;
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'dart:json' as JSON;
#observable String g_product_name;
#CustomTag('order-item')
class OrderItem extends PolymerElement with ObservableMixin {
#observable String product_name = g_product_name;
}
Map processString(String jsonString) {
// Create a map from json string
Map jsonParse = JSON.parse(jsonString);
print(jsonParse);
return jsonParse;
}
setGlobals(Map jsonMap) {
g_product_name = jsonMap["details"][0]["product_name"];
print(g_product_name);
}
main() {
var baseUrl = "https://example.com/add_item.json";
var params = window.location.search;
var fullUrl = baseUrl + params;
HttpRequest.getString(fullUrl)
.then(processString)
.then(setGlobals);
}
order_item.html
<polymer-element name="order-item">
<template>
<div>
<span>{{product_name}}</span>
</div>
</template>
<script type="application/dart" src="order_item.dart"></script>
</polymer-element>
Edit--Working Example
Your real issue is that
#observable String product_name = g_product_name;
doesn't set product_name to g_product_name, it sets product_name to the value of g_product_name at the time of assignment, which is null. Changing the value of g_product_name does not change the value of product_name.
I got around it by making a container for your global variable and making the property observable. That way you can set OrderItem property to the same object that your global variable is set to and it will bind the template to the property in your global variable.
add_item.json
{"details": [{"product_name": "foobar"}]}
order_item.html
<!DOCTYPE html>
<html>
<head>
<link rel="import" href="order_item.html">
<script src="packages/polymer/boot.js"></script>
</head>
<body>
<div class="mobile-menu-product-name">
<order-item></order-item>
</div>
</body>
</html>
order_item.dart
library orderitem;
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'dart:json' as JSON;
class GlobalVariable extends ObservableBase {
#observable
String g_product_name;
}
GlobalVariable globalVariable = new GlobalVariable();
#CustomTag('order-item')
class OrderItem extends PolymerElement with ObservableMixin {
GlobalVariable _globalVariable = globalVariable;
}
Map processString(String jsonString) {
// Create a map from json string
Map jsonParse = JSON.parse(jsonString);
print(jsonParse);
return jsonParse;
}
setGlobals(Map jsonMap) {
globalVariable.g_product_name = jsonMap["details"][0]["product_name"];
print(globalVariable.g_product_name);
}
main() {
HttpRequest.getString("add_item.json")
.then(processString)
.then(setGlobals);
}
It works for me. I replaced your main in order_item.dart with the following and it updated the order item to foobar.
main() {
setGlobals({"details":
[
{
"product_name": "foobar"
}
]
});
}
Obviously example.org/add_item.json isn't a working url, so I'd check to make sure the url is returning what you expect and the json structure is correct. Otherwise, it should work.

Calling function defined in library from dart webcomponent

The web application has following code in app.dart
library app;
import 'dart:html';
var _loginClass;
void main() {
_loginClass = 'hide_login'; //set style to hide login web component by setting display:none
}
void showLogin(e) {
_loginClass = 'show_login';
print("span clicked");
}
void hideLogin(e) {
_loginClass = 'hide_login';
}
calling hideLogin(e) function from App.dart hides the web component. but calling it from web component does not work.
css is defined as follows:
.hide_login {
display: none;
}
.show_login {
display = block;
}
It's weird that you have "display: none;" and "display = block;". The second is not valid syntax.
If that's not the right answer, try adding:
import 'package:web_components/web_components.dart';
And then call dispatch(); after setting _loginClass.
It would probably be more dartish to use
<template instantiate="bool expression">
This makes showing and hiding a custom element like a login component incredibly easy
example:
login.html
<html>
<body>
<element name="x-login" constructor="LoginComponent" extends="div">
<template instantiate="if showLogin">
...
<button on-click="validateLogin()">Login</button>
</template>
</element>
</body>
</html>
LoginComponent.dart
import "package:web_ui/web_ui.dart";
class LoginComponent extends WebComponent {
bool showLogin = true;
bool validateLogin() {
...
showLogin = false;
}
}
Check out http://www.dartlang.org/articles/dart-web-components/ for further details

Resources