I want to import an element and bind one of its properties. My import fails silently. I expect the value of userLocal to be an object. But, instead, userLocal is undefined.
my-app.html
<link rel="import" href="/src/app-state/state-user-local.html">
...
<state-user-local user-local="{{userLocal}}"></state-user-local>
...
<script>
/* #polymerElement */
class MyApp extends Polymer.Element {
static get is() { return 'my-app; }
static get properties() { return {
userLocal: {
type: Object,
notify: true,
},
...
}}
}
window.customElements.define(MyApp.is, MyApp);
</script>
with the following error message.
The element state-user-local is not defined
I know the import definition is correct because I am using VSCode and when I command+click the import it takes me to the correct file.
I know there is no problem with the <state-user-local> element itself because I successfully import it into other element/s in the app and obtain the expected value of userLocal there.
This problem sounds like what is described at the following links.
The element xxx is not defined (issue #54)
Recognize elements registered with MyElem.is (issue #540)
The first link discusses using "/* #polymerElement */ above the class" which is what I have tried (see above code) without success.
It seems to me that you didn't define the <state-user-local> element inside your file; you defined <my-app>. If you want to use the tag name <state-user-local> you need to define it as such.
<script>
class StateUserLocal extends Polymer.Element {
static get is() { return 'state-user-local'; }
static get properties() { return {
userLocal: {
type: Object,
notify: true,
},
...
}}
}
window.customElements.define(StateUserLocal.is, StateUserLocal);
</script>
Related
I've got a props file PropsFile.props.ts
export const props = {
/**
* Some comment
*/
name: {
type: String,
required: true,
}
};
export type Props = ExtractPropTypes<typeof props>;
I've got a vue file like this
<template>
some template here
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { props } from './PropsFile.props';
export default defineComponent({
name: "ComponentName",
props
});
</script>
Storybook doesn't create a document page with props and comments, like in that case if I define props inside vue file.
How could I solve this issue?
I assume you want to share these props with multiple components.
To do that, the "Vue 2" way is to use a mixin.
This mixin would be appropriately parsed by vue-docgen-api and rendered in storybook as you need.
Unfortunately, docgen does not execute your code. It parses it. So some sets of instructions might not render as props even if in the end they actually are.
Best explained with an example:
some-class.js
function SomeClass() { /* ... */ }
SomeClass.prototype.doSomething = function() { /* ... */ };
export function createSomeClass() {
return new SomeClass();
}
index.js
import { createSomeClass } from './some-class';
/**
* #param {SomeClass} someClass
*/
function foo(someClass) {
someClass.doSomething();
}
var someClass = createSomeClass();
someClass.doSomething();
This code leads to errors in VSCodes TypeScript checker and won't provide code completion for the class inside foo:
An alternative would be to export the class constructor and import it in index.js which gives me full code completion but adds a warning due to the unused import of the class:
What I also don't like about this solution is that it "leaks" the class to the outside which is not necessary otherwise due to the createSomeClass factory.
Is there some way to have full annotations & code completion without the unused import of the class?
Due to the fact that I am new in meteor/react I can't figure out how to initialize my state variable.
My problem is that I would like to get
my mongo collection through the createContainer from react-meteor-data (as described here),
use the initialized prop to intialize the state variable
But the prop in the constructor is empty. Only when I the "gotClicked" function is called the prop.allLists is filled with the data from mongo.
Does anyone know why? My guess the data is loaded asynchronously, so that the data is not available yet in the constructor.
What would be a better way to get the data?
import React, {Component, PropTypes} from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import {AllLists} from '../api/alllists.js'
export default class MyList extends Component {
constructor(props) {
super();
console.log(props.allLists)
console.log(this.props.allLists)
//allLists is empty
this.state = {
lists: props.allLists
}
}
gotClicked(){
console.log(this.props.allLists)
//allLists is filled
}
render() {
return (
<div className="container" onClick={this.gotClicked.bind(this)}>
</div>
);
}
}
MyList.propTypes = {
allLists: PropTypes.string.isRequired
}
export default createContainer(() => {
return {
allLists: AllLists.find({}).fetch()
};
}, MyList);
You're right, the data is loaded asynchronously, and it might not be available in the constructor. However, the callback function you pass to createContainer is evaluated again when the data is loaded, and it automatically updates the props of your component.
To catch this change, implement the componentWillReceiveProps function in your React component.
componentWillReceiveProps(nextProps) {
this.setState({
lists: nextProps.allLists
});
}
Docs here: https://facebook.github.io/react/docs/component-specs.html
Today, I trying to make my own annotation to use it in Angular2 project.
This annotation must add body class on specific component.
So, I've search on Angular source code, but it's so difficult to see where and how annotation was created.
For the moment, I've tried this :
export function BodyClass(classes: any): ClassDecorator{
classes = classes || {};
if(typeof classes === 'string'){
classes = {classes};
}
return function changeBodyClass(){
console.log('ici');
}
}
And my component :
import {Component} from "angular2/core";
import {RouterOutlet} from "angular2/router";
import {BodyClass} from "../core/annotations/body_class";
#Component({
selector: 'my-component',
template: `
<router-outlet></router-outlet>
`,
})
#BodyClass('test')
export class MyComponent{
}
My console log in the annotation was correctly fired, but I want to use "DOM" class from angular2/src/platform/dom/dom_adapter to add my classes, but DOM in undefined when on console log it (not need to instanciate it).
However, the DOM class works well directly in my component.
I add classes in ngOnInit function, and remove them on ngOnDestroy.
But, I want this behavior on many component, and I think a new annotation is the best way.
Maybe you have a better idea for this ? Or to work with DOM class on a annotation ?
Thanks !
It's not so obvious since you want to work at the component instance level and not at the component class one. So you need to wrap the corresponding constructor function.
export function MyComponentDecorator(value: string) {
return function (target: Function) {
var original = target;
function construct(constructor, args) {
var c : any = function () {
// Call the target constructor
var ret = constructor.apply(this, args);
// Add your additional processing
return ret;
}
c.prototype = constructor.prototype;
return new c();
}
// The new constructor
// Don't forget to add a name at this function
// since Angular requires it for the template compilation
var f : any = function WrappedComponent(...args) {
return construct(original, args);
}
f.prototype = original.prototype;
return f;
}
}
At this point, you wrap the component instance but you lose the component metadata. You need to copy them by hand:
f.prototype = original.prototype;
var annotations = Reflect.getMetadata('annotations', original));
Reflect.defineMetadata('annotations', annotations, f);
var properties = Reflect.getMetadata('propMetadata', original));
Reflect.defineMetadata('propMetadata', properties, f);
return f;
To use this decorator simply add it before or after the #Component one:
#MyComponentDecorator()
#Component({
selector: 'sub',
template: `
<div (click)="showMessage()">Test</div>
`
})
export class SubComponent {
(...)
}
You can notice that this decorator only copies the metadata at the component level and not the other ones like properties (with #Input)...
See this plunkr: https://plnkr.co/edit/PrAvliIpWTNFAtH33bHA?p=preview.
I am quite new to TypeScript and I experience a strange problem at the moment. I create an instance of my main class when the document is ready, using JQuery.
var main: MainApp;
$(document).ready(function () {
main = new MainApp();
});
The simplified MainApp Class:
class MainApp {
// Helper Objects
net: AppNetworking;
urlHelper: UrlHelper;
cat: Category;
// Construction
constructor() {
this.net = new AppNetworking();
this.urlHelper = new UrlHelper();
}
// Ajax Callback with Data needed to initialize the "cat" object
private AjaxCallback(categoryData){
this.cat = new Category(categoryData);
}
// Event Handler for an HTML-Element
// As it would be called anonymously in JS I decided to make it a static function
static onClickSendButton(): void{
// Using some members of the MainApp
var hostUrl: string = main.urlHelper.getQueryStringParam("HostUrl");
if (main.cat.isValidCategory()) {
main.sendCategory();
}
}
sendCategory(): boolean {
// Some logic to send data via AJAX
}
}
The function is being registered to the onClick Event of a Button on construction of the MainApp Class.
$("#btnSendCat").click(MainApp.onClickSendButton);
When the function onClickSendButton() gets called, it produces the error:
Uncaught TypeError: Cannot read property 'isValidCategory' of undefined
When debugging, the urlHelper Instance is defined, but the cat Instance is undefined. As I do not touch the instance cat anywhere in my application, I'm really confused how it is undefined. Also when checking the main variable all members are defined!
Am I doing anything illegal here? Could there be issues with that code?
Completely revised answer. I actually answered with the two most common scenarios for this error, but actually your problem is different.
The usual answers are
Make sure you are referencing .js files, not .ts files
Make sure you are loading scripts in the correct order
In your case, this is not the problem and your code is sufficient to recreate the issue.
I have put together the following test, filling in the blanks - and it works as expected.
app.ts
declare var main: MainApp;
class AppNetworking {
}
class UrlHelper {
getQueryStringParam(input: string) {
console.log('Got here getQueryStringParam');
return input;
}
}
class Category {
isValidCategory() {
console.log('Got here isValidCategory');
return true;
}
}
class MainApp {
// Helper Objects
net: AppNetworking;
urlHelper: UrlHelper;
cat: Category;
// Construction
constructor() {
this.net = new AppNetworking();
this.cat = new Category();
this.urlHelper = new UrlHelper();
}
// Event Handler for an HTML-Element
// As it would be called anonymously in JS I decided to make it a static function
static onClickSendButton(): void{
// Using some members of the MainApp
var hostUrl: string = main.urlHelper.getQueryStringParam("HostUrl");
if (main.cat.isValidCategory()) {
main.sendCategory();
}
}
sendCategory(): boolean {
// Some logic to send data via AJAX
return true;
}
}
index.html snip
<div id="btnSendCat">BTN SEND CAT</div>
<script src="app.js"></script>
<script src="Scripts/jquery-2.1.1.min.js"></script>
<script>
var main;
$(document).ready(function () {
main = new MainApp();
$("#btnSendCat").click(MainApp.onClickSendButton);
});
</script>
The result of running this test is the following output in the console window:
"Got here getQueryStringParam" app.js:10
"Got here isValidCategory" app.js:19
I left some important parts of my App out, I'm sorry. Later in the project I used to reinitialize that Category Object. This re initialization was done in an AJAX-Callback Function. This function runs outside of my Object and this wont be my MainApp Class but the Window. I think it's what you call an anonymous function in JavaScript.
I fixed that issue by taking use of my global main Variable
class MainApp {
// Called anonymous so it should be a static function
private AjaxCallback(categoryData){
// this.cat = new Category(categoryData); ! this will be the Window Instance and not a MainApp Instance
main.cat = new Category(categoryData); // Initialization using the "main" variable
}
}
The call in my onClickSendButton Method to this.cat succeeds now, as this.cat was reinitialized correctly.
This video helped me a lot in my researches: Understanding "this" in TypeScript