I have a component that performs a task, but then needs to refresh the data (a method callable from the controller).
Is it possible to access the controller methods and properties from a component?
Is it available through Scope? Parent/root scope?
(Also noticed I'm using a directive as well which would love to access the parent scope)
I used $broadcast ... Not sure if that's the proper way to do it or not. I imagine trying to access the controller from component/directive is circular? So maybe $broadcast is what I want?
...For methods it makes sense I guess...But I do feel like I should be able to get a property from the controller. I must be doing something wrong if I can't see the parent scope...
You have to set the visibility on the parent then you can inject the parent to the child. (I'm only on my phone and limited possibilities) AFAIK SO has a question+answer already.
It sounds like you have one controller and one component which is a child element of the controller. In this case you can inject the controller in the component's constructor:
#NgController(...)
class MyController {
refresh() {
...
}
}
#NgComponent(...)
class MyComponent {
MyController _myController;
MyComponent(MyController this._myController);
doSomething() {
_myController.refresh();
}
}
Components should basically be reusable and therefore shouldn't have any reference to the calling controller. Here is one solution where two separate controllers use one component to modify controller's property. I'm not sure have I used watch-function properly or is there better way to do this.
Idea comes from this question and answers:
AngularDart components and model binding
edit: ops, sorry: One controller and two different properties.
ang_testi.dart:
import 'package:angular/angular.dart';
import 'package:di/di.dart';
#NgComponent(
selector: 'my-component'
)
class MyComponent {
NgModel _ngModel;
Scope _scope;
MyComponent(this._ngModel, this._scope){
_scope.$watch(() => _ngModel.modelValue, (value) => onC(value));
}
onC(value){
if(value!=null) _ngModel.modelValue=value.toUpperCase();
return _ngModel.modelValue;
}
}
#NgController(
selector: '[main-test]',
publishAs: 'ctrl')
class MainTestController {
String msg;
String msg2;
Scope _scope;
MainTestController(this._scope){
_scope.$watch(() => msg, (value) => onMsgC(value));
}
onMsgC(value){
print("msg change:$value");
}
}
class MyAppModule extends Module {
MyAppModule() {
type(MainTestController);
type(MyComponent);
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}
ang_testi.html:
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<title>ng-model test</title>
<link rel="stylesheet" href="ang_testi.css">
</head>
<body main-test>
<my-component ng-model="ctrl.msg"></my-component>
<my-component ng-model="ctrl.msg2"></my-component>
<p><input type="text" ng-model="ctrl.msg"></p>
<p>Hello world from {{ctrl.msg}}!</p>
<p><input type="text" ng-model="ctrl.msg2"></p>
<p>Hello world from {{ctrl.msg2}}!</p>
<script src="packages/shadow_dom/shadow_dom.min.js"></script>
<script type="application/dart" src="ang_testi.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
I'm sorry if I'm polluting this thread.
I found a much simpler way to create a component, which can manipulate data in the calling controller. In the following example the #NgComponent transforms the string from the calling controller to uppercase and preserves a twoway binding.
The key to preserve binding is to use the variable of type: List, Map or Class (I believe also Set and Queue will work). With these types Dart uses "pointers", not values (sorry if the terms are not correct).
The correct type of variable makes also following code work (if you use for example type: String, bindings are not working): https://github.com/angular/angular.dart/issues/264
ang_testi.dart
import 'package:angular/angular.dart';
import 'package:di/di.dart';
#NgComponent(
selector: 'my-component'
)
class MyComponent {
List _test;
#NgTwoWay('test')
set test(List list){
_test=list;
}
List get test{
_test[0]=_test[0].toUpperCase();
return _test;
}
}
#NgController(
selector: '[main-test]',
publishAs: 'ctrl')
class MainTestController {
List msg=[""];
List msg2=[""];
}
class MyAppModule extends Module {
MyAppModule() {
type(MainTestController);
type(MyComponent);
}
}
void main() {
ngBootstrap(module: new MyAppModule());
}
ang_testi.html
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<title>ng-model test</title>
<link rel="stylesheet" href="ang_testi.css">
</head>
<body main-test>
<my-component test="ctrl.msg"></my-component>
<my-component test="ctrl.msg2"></my-component>
<p><input type="text" ng-model="ctrl.msg[0]"></p>
<p>Hello world from {{ctrl.msg[0]}}!</p>
<p><input type="text" ng-model="ctrl.msg2[0]"></p>
<p>Hello world from {{ctrl.msg2[0]}}!</p>
<script src="packages/shadow_dom/shadow_dom.min.js"></script>
<script type="application/dart" src="ang_testi.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
Related
I'm trying to create a component in AngularDart 1.0:
index.html:
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>data_generator_front</title>
</head>
<body>
<my-form></my-form>
<label>Name: </label>
<input type="text" ng-model="myProperty" placeholder="Enter your name here">
<hr>
<h1 ng-cloak>Hello {{myProperty}}</h1>
<input type="submit" ng-click="method1()">
<script type="application/dart" src="index.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
index.dart:
import "package:angular/angular.dart";
import "package:angular/application_factory.dart";
import "my_form.dart";
#Injectable()
class RootContext{
String myProperty = "jeahhhhhhhh";
void method1(){
print("method1");
}
}
main(){
applicationFactory()
.rootContextType(RootContext)
.addModule(new DateFormModule())
.run();
}
my_form.html
<form>
<label>Years:</label>
<input type="text" placeholder="begin">
<input type="text" placeholder="end">
<input type="checkbox">
<input type="submit">
</form>
my_form.dart
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html")
class DateFormComponent {}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
Please correct what i'm saying.
I have a class "RootContext" that holds the property 'myProperty' and a method "method1". This class is annotated by #Injectable() which enable the class to be handle by angulardart. like that all the properties/methods are availlable in the html in {{}}, ng-model, ng-click.... last, rootContextType(RootContext) makes RootContext the rootcontext of the app. Is that correct ?
Now I would like to create a Component. (my_form.dart and my_form.html)I succeed to display the html of my component which is great.
I would to pre fill the input (the one with placeholder="begin") with a value like "1980"
here is my attempt:
my_form.dart:
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html")
class DateFormComponent {
String years_begin = "1980";
}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
my_form.html
<form>
<label>Years:</label>
<input type="text" placeholder="begin" ng-model="years_begin">
<input type="text" placeholder="end">
<input type="checkbox">
<input type="submit">
</form>
And that crash with :
...
Missing getter: (o) => o.years_begin
...
ok let's create a getter then: String get years_begin => _years_begin;
but still have the same error...
so #Component() is not like #Injectable() it doesn't turn on all the field/methods to be handle by the view. so how to turn them on ? it's seems like #NgAttr(attrName),
#NgOneWay(attrName), #NgTwoWay(attrName) do the job
let's try this
class DateFormComponent {
#NgAttr("years_begin")
String years_begin = "1980";
}
--> no more exception but the field is not filled
class DateFormComponent {
#NgOneWay("years_begin")
String years_begin = "1980";
}
--> do the job !
class DateFormComponent {
#NgTwoWay("years_begin")
String years_begin = "1980";
}
--> as well
is NgOneWay synchronize model to view ?
is NgTwoWay synchronize model to view and view to model ?
ngAttr seems more to be for the attribute passed in in the HTML like and just at init time ?
let's switch to method now here is my change
my_form.html
<form>
<label>Years:</label>
<input type="text" placeholder="begin" ng-model="years_begin">
<input type="text" placeholder="end">
<input type="checkbox">
<input type="submit" ng-click="method2()">
</form>
my_form.dart
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html")
class DateFormComponent {
#NgTwoWay("years_begin")
String years_begin = "1980";
method2(){
print("method2");
}
}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
it crash with : No getter for 'method2'.
What is the equivalent of NgOneWay ngTwoway for methods ?
Do not hesitate to give me UP TO DATE links and information about angulardart & dart as there are not a lot of documentation up to date even the angulardart website...
Thanks to an answer here I found an answer regarding to the No getter for 'method2' problem.
The transformer struggled so we need to pass extra information: in my component I set the exportExpressions property like this:
my_form.dart
import "package:angular/angular.dart";
#Component(
selector: "my-form",
templateUrl: "my_form.html",
exportExpressions: const ["method2"])
class DateFormComponent {
#NgTwoWay("years_begin")
String years_begin = "1980";
method2(){
print("method2");
}
}
class DateFormModule extends Module {
DateFormModule(){
bind(DateFormComponent);
}
}
I think it's related to tree shaking and that way the transformer exact the 'method2'
I have a class "RootContext" that holds the property 'myProperty' and a method "method1". This class is annotated by #Injectable() which enable the class to be handle by angulardart. like that all the properties/methods are availlable in the html in {{}}, ng-model, ng-click.... last, rootContextType(RootContext) makes RootContext the rootcontext of the app. Is that correct ?
#Injectable() has nothing much to do with Angular binding ({{}}), it is only for the dependency injection system used in Angular and the transformer that generates code to avoid Mirrors/Reflection in production code (which has performance and code bloat issues)
You also need to add a transformers configuration to make this work
see https://github.com/vicb/angular.dart.tutorial/blob/1013-v1/Chapter_06/pubspec.yaml#L12 for an example.
(Angular.dart transformers need to be applied for development too, not only for production)
In the following example, when the Update Class button is clicked, {{testClass.someProperty}} is updated with the new class' someProperty, but updating testClass.someProperty does not update {{testClass.someProperty}} in the template.
I thought making the TestClass #observable would make its properties observable and when updating the property, the property in the template would change.
What would be the correct way to bind TestElement.testClass.someProperty to {{testClass.someProperty}}, and as an extension, any class' properties to a Polymer Element template?
test_class.dart
library test;
import "package:polymer/polymer.dart";
#observable
class TestClass extends ObservableBase {
String someProperty;
TestClass(this.someProperty);
}
test_element.dart:
library test_element;
import "package:polymer/polymer.dart";
import "test_class.dart";
#CustomTag("test-element")
class TestElement extends PolymerElement with ObservableMixin {
#observable
TestClass testClass = new TestClass("original");
void updateClass() {
testClass = new TestClass("xyz");
}
void updateProperty() {
testClass.someProperty = "foobar";
}
}
test_element.html
<polymer-element name="test-element">
<template>
{{testClass.someProperty}}
<button on-click="updateProperty">Update Property</button>
<button on-click="updateClass">Update Class</button>
</template>
<script type="application/dart" src="test_element.dart"></script>
</polymer-element>
test.dart
library test;
int main() {
}
test.html:
<!DOCTYPE html>
<html>
<head>
<link rel="import" href="test_element.html" />
<script src="packages/polymer/boot.js"></script>
</head>
<body>
<test-element></test-element>
<script type="application/dart" src="test.dart"></script>
</body>
</html>
I got it to work by moving #observable from the class to the property. I thought annotating the class with #observable was supposed to be the same as annotating all the properties as observable, but that didn't work:
test.dart
library test;
import "package:polymer/polymer.dart";
class TestClass extends ObservableBase {
#observable
String someProperty;
TestClass(this.someProperty);
}
After overriding three lifecycle methods of WebComponent: created(), inserted(), and removed(), I can see that the first two are called consistently but removed() is never called. Is there anything special that needs to be done so that removed() is called? Or is it simply never called?
The removed() method is called when a custom element is removed from the DOM. Here is a small program that demonstrates the use of the created(), inserted(), and removed() lifecycle events.
Create a Dart web application with an index.html file that looks like this:
<!DOCTYPE html>
<html>
<head>
<title>index</title>
<link rel="import" href="my_element.html">
<script src="packages/polymer/boot.js"></script>
</head>
<body>
<div id='container'><my-element></my-element></div>
<script type="application/dart">
import 'dart:html';
void main() {
query('#container').onClick.listen((event) {
event.target.remove();
});
}
</script>
</body>
</html>
This file imports and displays a custom element, <my-element>.
Define the following file that defines <my-element>:
<!DOCTYPE html>
<html>
<body>
<polymer-element name="my-element">
<template>
<p>The name is {{person.name}}</p>
</template>
<script type="application/dart" src="my_element.dart"></script>
</polymer-element>
</body>
</html>
And define the accompanying Dart file that demonstrates the lifecycle methods getting called:
import 'package:polymer/polymer.dart';
class Person extends Object with ObservableMixin {
#observable String name;
Person(this.name);
}
#CustomTag("my-element")
class MyElement extends PolymerElement {
#observable Person person = new Person('Shailen');
void created() {
super.created();
print('created');
}
void inserted() {
print('inserted');
}
void removed() {
print('removed');
}
}
When you run index.html, you will see a paragraph with some text in it. The created() and inserted() lifecycle methods are called, and 'created' and 'inserted' messages print in the console. When you click on the div that contains the custom element, the element is removed, the removed() lifecycle method is called, and 'removed' prints in the console.
Hope this helps.
I have a class field that is a String and I want to use a text input to modify that field. How can I use #observable with polymer.dart to do this?
Here is the class field I would like to sync-up with the UI:
class Person {
#observable String name;
Person(this.name);
}
Import the polymer.dart file and mix in the ObservableMixin into Person. Extend PolymerElement, and also use a #CustomTag annotation.
Here is what the dart file using #observable with a custom element could look like:
import 'package:polymer/polymer.dart';
class Person extends Object with ObservableMixin {
#observable String name;
Person(this.name);
}
#CustomTag("custom-element")
class CustomElement extends PolymerElement {
#observable Person person = new Person('John');
}
In the associated .html file, use {{}} syntax to create the binding with the #observable field:
<!DOCTYPE html>
<html>
<body>
<polymer-element name="custom-element">
<template>
<label> Name: <input value="{{person.name}}"></label>
<p>The name is {{person.name}}</p>
</template>
<script type="application/dart" src="element.dart"></script>
</polymer-element>
</body>
</html>
This element can be used in the following manner (note the link to boot.js):
<!DOCTYPE html>
<html>
<head>
<title>index</title>
<link rel="import" href="element.html">
<script src="packages/polymer/boot.js"></script>
</head>
<body>
<custom-element></custom-element>
<script type="application/dart">
void main() {}
</script>
</body>
</html>
I looked at the network tab on Chrome and found that my breeze api for metadata is not getting called and thus all my methods of createEntity etc are failing.
[HttpGet]
public string Metadata()
{
return _context.Metadata();
}
I have followed the todo example and have enabled CORS Support and just about everything, I can see the json metadata if i navigate to /api/breeze/metadata
I am using VS2012 Web Express and gotten all the packages thru nuget
dataservice.js
app.dataservice = (function (breeze) {
var serviceName = 'api/breeze'; // route to the same origin Web Api controller
var manager = new breeze.EntityManager(serviceName);
return {
createTodo: createTodo,
saveChanges: saveChanges
};
function createTodo(initialValues) {
return manager.createEntity('BreezeItem', initialValues);
}
function saveChanges() {
return manager.saveChanges()
.then(function () { alert('good') })
.fail(function () { alert('bad') })
}
})(breeze);
index.html
`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1"/>
<title>Breeze Todos with KO</title>
</head>
<body>
<div id="applicationHost">
<header>
<h1>Breeze Todos</h1>
<h2>with Knockout</h2>
<form data-bind="submit: addItem">
<input type="text" data-bind="value: newBreeze, valueUpdate: 'afterkeydown'" placeholder="What needs to be done?">
</form>
</header>
</div>
<!-- 3rd party libraries -->
<script src="Scripts/jquery-1.8.3.js"></script>
<script src="Scripts/knockout-2.2.1.debug.js"></script>
<script src="Scripts/q.js"></script>
<script src="Scripts/breeze.debug.js"></script>
<script src="Scripts/breeze.savequeuing.js"></script>
<!-- App libraries -->
<script>app = {};</script>
<script src="app/dataservice.js"></script>
<script src="app/viewModel.js"></script>
</body>
</html>
viewmodel.js
app.viewModel = (function ( dataservice) {
var vm = {
newBreeze: ko.observable(""),
addItem: addItem
};
return vm; // done with setup; return module variable
//#region private functions
function addItem() {
var item = dataservice.createTodo(
{
BreezeName: vm.newBreeze()
}
);
dataservice.saveChanges().fail(addFailed);
vm.newTodo("");
function addFailed() {
console.log("Failed")
}
}
function extendItem(item) {
if (item.isEditing) return; // already extended
item.isEditing = ko.observable(false);
}
})(app.dataservice);
// Bind viewModel to view in index.html
ko.applyBindings(app.viewModel);
Breeze makes the metadata call under two conditions, either implicitly as part of the first query in an application or explicity via the EntityManager.fetchMetadata method. This means that if you are not performing a query as your first action, then you will need to make a 'fetchMetadata' call instead. Remember that this is an async call that returns a 'promise', just like a query, so you will need to perform any calls to your "createEntity" methods when the promise resolves (i.e. within the 'then' call.