hannahhoward / a1atscript Goto Github PK
View Code? Open in Web Editor NEWThe Angular 2 Polyfill
License: MIT License
The Angular 2 Polyfill
License: MIT License
If we have:
<component prop="someText">
<component bind-prop="someExpression">
That will throw the error "Not allowed to use prop and bind-prop simultaneously" even though they are used on different instances. This was unintentionally added.
would be great to know how to use it with jspm. (I tried several ways but without success)
I am using a1atscript with JSPM + Typescript in a test project
I assumed I can import a1atscript
since you added format global
in package.json
but format global
is causing errors. I have remove following to make it work.
jspm_packages/npm/[email protected]/dist/a1atscript.bundle.js
/* */
"format global";
Ready for your first tech support issue? :) Cool project by the way!!
I'm trying to use the Angular 2 syntax, like this:
// main.js
import 'babelify/polyfill';
import 'angular';
import {AsModule, Component, View, bootstrap} from 'a1atscript';
@AsModule('list', [])
@Component({
selector: "app"
})
@View({
inline: "<div><h1>App Loaded</h1></div>"
})
class ListApp {
}
bootstrap(ListApp);
// index.html
<app></app>
I am seeing nothing, am I missing something? I see within your bootstrap function here...
export function bootstrap(appModule, appPrefix = "") {
var injector = new Injector(appPrefix);
var moduleName = injector.instantiate(appModule);
Router.routeInitializer.initialize(moduleName, appModule);
}
...you are initializing the module with the Router. Is that the ng1 router or the ng2 router? I'd prefer not to use the ng2 router just yet. In fact I'd like to not use any router just yet. Also when I tried to add ng-app="list'
to my index.html, that threw an error.
I'm seeing go into your library's code and it gets all the way through RouteInitializer.initialize, but then no more code seems to run...
See: https://angular.io/docs/js/latest/api/annotations/DirectiveAnnotation-class.html. There are various dependencies you can inject into the controller class constructor.
directive:DirectiveType
: a directive on the current element only@Ancestor() directive:DirectiveType
: any directive that matches the type between the current element and the Shadow DOM root. Current element is not included in the resolution, therefore even if it could resolve it, it will be ignored.@Parent() directive:DirectiveType
: any directive that matches the type on a direct parent element only.@Query(DirectiveType) query:QueryList<DirectiveType>
: A live collection of direct child directives.@QueryDescendants(DirectiveType) query:QueryList<DirectiveType>
: A live collection of any child directives.In angular 1:
require: 'dependency'
. So no implementation needed.Thoughts on 4 & 5: Perhaps an api like this would be possible, where we auto-generate a $Query method on every component class that would help with this:
@Component({ selector: 'child-a' })
class ChildA {
foo() { console.log('foo') }
}
@Component()
@View({
inline: `
<child-a/>
<child-a/>
`
})
class MyComponent {
constructor() {
let childrenA = this.$Query(ChildA);
console.log(childrenA.length); // outputs 2
console.log(childrenA[0].foo()); // outputs 'foo'
}
}
Similar to #19. Except, where appInjector
specifies controller-injected dependencies, directives
instead specifies which directives (aka Components) are needed for the view. Both are needed as module dependencies though.
See: https://angular.io/docs/js/latest/api/annotations/ComponentAnnotation-class.html for more info on @view.
Instead of using @AsModule to add our component directive dependencies let's use @view directives
.
For example, currently we do this:
@Component({ selector: 'foo' })
class Foo {}
@AsModule('Bar', [Foo])
@Component({
selector: 'bar'
})
@View({
inline: `<foo></foo>`
})
class Bar { }
We have to add the Foo component as a module dependency on the Bar component's module. Seems like we could achieve the same thing while also following angular 2 api conventions.
We should be able to do this:
@Component({ selector: 'foo' })
class Foo {}
@Component({
selector: 'bar'
})
@View({
directives: [Foo],
inline: `<foo></foo>`
})
class Bar { }
Notice I left out the @AsModule because it should be able to grab the module off of the Foo class passed into the directives
property.
When I'm using the Component events config it hooks up the handler with an incorrect this
context. I would expect this
to reference the App class instance, but it actually references the Home class instance.
@Component({
selector: 'home'
})
@View({
template: "<p>Home</p>",
// Here I'm setting up a 'foo' event
events: {
foo: "foo"
}
})
class HomeComponent {
constructor() {
// See here that I'm adding a property to Home
this.wtf = "wat?";
}
}
@AsModule('AppModule', [HomeComponent])
@Component({
selector: "app"
})
@View({
// Here I'm using the on-foo event and passing a reference to the App's doFoo method
inline: `<home on-foo="doFoo"></home>`
})
class AppComponent {
doFoo() {
// Here is where things fell apart for me and 'this' was not what I expect
console.log(this) // HomeComponent {wtf: "wat?"}, expected an instance of AppComponent
}
}
bootstrap(AppComponent)
Given using angular 1.4.1 and a component such as this:
import {Component, View} from 'a1atscript';
@Component({
selector: "child",
properties: {
data: 'data'
}
})
@View({
inline: `<div>{{child.data | json}}</div>`
})
export default class Child { }
When I use it in another component, like this:
import {Component, View} from 'a1atscript';
@Component({
selector: "parent"
})
@View({
inline: `<child bind-data="parent.dataForChild"></child>`
})
export default class Parent {
constructor() {
this.dataForChild = {foo: 'bar'};
}
}
I end up seeing nothing rendered out in Child. When I look at it in dev tools, I see it set to undefined
, then {foo: 'bar'}
, then (and here's the problem) undefined
again.
What is happening is this:
data
and bind-data
.undefined
, setting Child.data to undefined
{foo: 'bar'}
, setting Child.data to undefined
I think the workaround needs to be that a1atscript disallows usage of both the regular attr (attr) and the bound version of the attr (bind-attr). Obviously both attrs need to be set up, but then when the user uses one, it should somehow cancel out the other somehow. So if I use bind-attr, then attr knows to just stay out of the way.
I tried to add some links to your router example in the readme. This is what I ended up with:
@Component({})
@View({
template: "<p>Sub</p>"
})
class SubComponent {
}
@Component({})
@View({
template: "<p>Home</p><ng-viewport></ng-viewport>"
})
@RouteConfig({
path: "/sub", component: SubComponent
})
class HomeComponent {
}
// the AsModule annotation is an extra need to setup Angular's module system on the top level component for now
@AsModule('App', ['ngNewRouter', HomeComponent, SubComponent])
@Component({
selector: "awesome"
})
@View({
template: `
<a ng-link="sub">sub</a>
<a router-link="home">home</a>
<ng-viewport></ng-viewport>
`
})
@RouteConfig({
path: "/home", component: HomeComponent
})
class AppComponent {
}
bootstrap(AppComponent)
Notice the ng-link
and router-link
in the AppComponent
template. I wasn't sure what the correct directive name was. This seems to line up with the official documentation, but when I load the page, the ng-link
has an href
of "." and therouter-link
has no href
. Is there something wrong with the way I'm using these directives, or is a1atscript doing something unexpected with the RouteConfig
?
As is, every annotation needs two closely coupled classes, but there doesn't seem to be a reason for that.
You need the annotation itself and the annotationInjector that does the actual work. The injector tends to be in a class heirarchy, but the annotation doesn't. There isn't any overlap of methods.
Consider unifying these classes - just apply the instatiate method of found annotations as we go.
If I understand it correctly, _getDependencyType(dependency) stops at the first annotation it finds. The initial motivation (replace angular.thing calls) only needs that, it might be nice to be able to build up a library of annotations, which means handling multiples.
Which opens the question: what if a single class is annotated (e.g.) @service and @controller?
Stock Angular 1.3:
angular.module(...).filter("name", function(){
return function(string, expression){ ... }
})
Since every filter follows this pattern, and stateful or injected filters are discouraged, perhaps A1 could do:
@Filter
export default function(string, expression){ ... }
(at the moment, the wrapping provider function is required)
The latest Angular 2 has renamed injectables
to appInjector
. See: https://angular.io/docs/js/latest/api/annotations/ComponentAnnotation-class.html.
In addition to renaming this, I think we should consider allowing appInjector
to take not only string-based ng1 injectables (e.g. '$http'
) but also a1atscript created injectables (e.g. Greeter
).
For example, currently we do this:
@Service('greeter')
class Greeter {}
@AsModule('GreetCmpt', [Greeter])
@Component({
selector: 'greet',
injectables: ['$http', 'greeter']
})
class HelloWorld {
constructor($http, greeter) {
this.greeter = greeter;
}
}
We have to add the Greeter service as a module dependency on the Greet component's module. Then also add it to injectables
. Seems like we could reduce some boilerplate here.
We should be able to do this:
@Service
class Greeter {}
@Component({
selector: 'greet',
appInjector: ['$http', Greeter]
})
class HelloWorld {
constructor($http, greeter) {
this.greeter = greeter;
}
}
In this example I've renamed injectables
to appInjector
but I've now added the ability to not only inject string-based things like '$http'
but also the Greeter service. This more closely matches how ng2 uses classes instead of strings. Also, notice I left out the @AsModule because it should be able to grab the module off of the Greeter class passed into the appInjector
.
So the acceptance criteria for this feature would be:
injectables
to appInjector
appInjector
to the dependencies of the auto-generated module of the host class.@AsModule("GreetComponent")
The 'properties' config property in @component has been changed to an array syntax. So we need to update it to instead of:
@Component({
properties: {
foo: 'foo',
bar: 'baz'
}
})
We need it to be:
@Component({
properties: ['foo', 'localName: externalName']
})
I might fix this soon, but wanted to post it in case anyone wants to submit a pull request. Shouldn't be hard.
The bower.json file references the following files:
"main": [
"dist/a1atscript.js",
"dist/a1atscript/annotations.js",
"dist/a1atscript/DirectiveObject.js",
"dist/a1atscript/Injector.js",
"dist/a1atscript/injectorTypes.js",
"dist/a1atscript/ToAnnotation.js",
"dist/a1atscript/Router.js",
"dist/a1atscript/bootstrap.js",
"dist/a1atscript/AnnotationFinder.js",
"dist/a1atscript/ng2Directives/Component.js",
"dist/a1atscript/ng2Directives/ComponentInjector.js",
"dist/a1atscript/ng2Directives/Ng2Directive.js",
"dist/a1atscript/ng2Directives/Ng2DirectiveDefinitionObject.js",
"dist/a1atscript/ng2Directives/SelectorMatcher.js",
"dist/a1atscript/ng2Directives/BindBuilder.js",
"dist/a1atscript/ng2Directives/EventsBuilder.js",
"dist/a1atscript/ng2Directives/PropertiesBuilder.js",
"dist/a1atscript/router/ComponentMapper.js",
"dist/a1atscript/router/RouteConfig.js",
"dist/a1atscript/router/RouteInitializer.js",
"dist/a1atscript/router/RouteReader.js"
],
Those are not part of the repo since the files are bundled.
See: https://angular.io/docs/js/latest/api/annotations/DirectiveAnnotation-class.html
@directive is basically a @component without a view. So its a directive that alters the behavior of an element. So we should be able to use it just like @component, like this;
@Directive({
selector: '[dependency]',
properties: ['id']
})
class Dependency { }
I know right now @directive is an ng1 decorator for basically doing ng1 style directive declaration. But I think a1atscript should focus on being a pure ng2 polyfill (my two cents).
See: http://victorsavkin.com/post/119943127151/angular-2-template-syntax. Scroll down to where he implements ng-model from scratch. Here's his code:
@Directive({
selector: '[ng-model]',
properties: ['ngModel'],
events: ['ngModelChanged: ngModel'],
host: {
"[value]": 'ngModel',
"(input)": "ngModelChanged.next($event.target.value)"
}
})
class NgModelDirective {
ngModel:any; // stored value
ngModelChanged:EventEmitter; // an event emitter
}
See how it's possible to add host events, surrounded with '( )', and host property bindings, surrounded with '[ ]'. I feel like this should be possible. Behind the scenes we can get a reference to the element, perhaps from the link function and add the watches and/or event listeners and bind them to the controller class fields and methods.
Depends on #20
To reproduce:
import {AsModule, Component, View} from 'a1atscript';
@Component({
selector: "parent",
properties: {
data: 'data'
}
})
@View({
inline: `
<div>This data won't show: {{ parent.data | json }}</div>
<child bind-data="parent.data"></child>
`
})
export default class ParentComponent { }
@Component({
selector: "child",
properties: {
data: 'data'
}
})
@View({
inline: `<div>This data will show: {{ child.data | json }}</div>`
})
export default class ChildComponent { }
@Component({
selector: "app"
})
@View({
inline: `<parent bind-data="app.data"></parent>`
})
export default class App {
constructor() {
this.data = {foo: 'bar'}
}
}
In this scenario, with data cascading from the top-level all the way through two nested components, we are seeing an issue where the data is not shown in the intermediate levels, parent. We've tracked this to a piece of angular.js code that is trying to access the value of our hidden properties—"_@prop" and "=_prop". However, there are now getters set up for those properties so they are returning undefined and forcing a codepath that believes the data needs to be inherited from the scope chain.
Adding getters should fix this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.