Angular2 injected service going undefined - dependency-injection

I was following this tutorial and reached the below code with searches wikipedia for a given term. The below code works fine and fetches the search result from wikipedia.
export class WikiAppComponent {
items: Array<string>;
term = new Control();
constructor(public wikiService: WikiService) { }
ngOnInit() {
this.term.valueChanges.debounceTime(400).subscribe(term => {
this.wikiService.search(term).then(res => {
this.items = res;
})
});
}
}
But when I refactored the and moved the code for search to a separate function it is not working. this.wikiService inside the search function is going undefined. Can you throw some light on why it is going undefined?
export class WikiAppComponent {
items: Array<string>;
term = new Control();
constructor(public wikiService: WikiService) { }
search(term) {
this.wikiService.search(term).then(res => {
this.items = res;
});
}
ngOnInit() {
this.term.valueChanges.debounceTime(400).subscribe(this.search);
}
}

You are having a scope issue, "this" inside your callback is not refering to your page. Change your function callback like this:
this.term.valueChanges.debounceTime(400).subscribe(
(term) => {
this.search(term);
});

Related

Angular 2 redirect if user have shop

How i can make redirect if User -id(this.authTokenService.currentUserData.id) have shop (owner_id) .
Shop.component.ts
owner_id :number;
private sub: any;
ngOnInit() {
this.sub = this.httpService.getShops().subscribe(params => {
this.owner_id = this.authTokenService.currentUserData.id ;
console.log(this.sub)
console.log(this.owner_id)
});
if (this.sub) {
this.router.navigate(['/profile']);
}
Http.service.ts
getShops(){
return this.http.get('http://localhost:3000/shops.json')
}
I use Rails 5 for api and auth token. Thanks for help. Sorry for my English
I can't complete grasp your intentions here, but this code seems to be what you're after. Hope this helps
ngOnInit() {
this.getUserShops(this.authTokenService.currentUserData.id)
.subscribe((ownerShops) => {
if (ownerShops.length > 0) {
// User has at least 1 shop
this.router.navigate(['/profile']);
} else {
// User has no shops
}
})
}
getUserShops(ownerId: number): Observable<any> {
return this.httpService
.getShops()
// Map the returned array to a filtered subset including only the owners id
.map((shops: any[]) => shops.filter(shop => shop.owner_id === ownerId));
}
// http.service.ts
export class HttpService {
getShops(): Observable<Shop[]> {
return this.http.get('http://localhost:3000/shops.json')
.map((res: Response) => res.json())
}
}
export interface Shop {
owner_id: number | null;
}
EDIT: Added update to show example http.service.ts typings
private sub: any;
ngOnInit() {
this.httpService.getShops()
.subscribe(params => {
// code here is executed when the response arrives from the server
this.owner_id = this.authTokenService.currentUserData.id ;
console.log(this.sub)
console.log(this.owner_id)
if (this.sub) {
this.router.navigate(['/profile']);
}
});
// code here is executed immediately after the request is sent
// to the server, but before the response arrives.
}
If the code depends on sub that you get from subscribe, then you need to move the code inside the subscribe callback, otherwise it will be executed before this.sub gets a value.
try this code.
owner_id :number;
private sub: any;
ngOnInit() {
this.httpService.getShops().subscribe(
response => {
this.sub = response
if (this.sub.length>0) {
this.router.navigate(['/profile']);
}
}
});

How to setState in AsyncStorage block with react native?

I want setState in AsyncStorage block but there is a error: undefined is not an object(evaluating 'this.setState').
constructor(props){
super(props);
this.state={
activeID:this.props.data.channel[0].id
};
}
componentDidMount() {
AsyncStorage.getItem(this.props.data.type,function(errs,result){
if (!errs) {
if (result !== null) {
this.setState({activeID:result});
}
}
});
}
_buttonPressed = (id,name,index) => {
this.setState({activeID:id});
AsyncStorage.setItem(this.props.data.type,id,function(errs){
if (errs) {
console.log('error');
}
if (!errs) {
console.log('succeed');
}
});
}
Any help will be appreciate, thanks.
This is a binding issue. Try the following:
componentDidMount() {
AsyncStorage.getItem(this.props.data.type, (errs,result) => {
if (!errs) {
if (result !== null) {
this.setState({activeID:result});
}
}
})
}
The reason for this is that functions declared with the function specifier create a context of their own, that is, the value of this is not the instance of your component. But "fat arrow" functions do not create a new context, and so you can use all methods inside. You could as well bind the function in order to keep the context, but in this case I think that this solution is much cleaner.
I find another solution, but martinarroyo's answer is much more cleaner.
componentDidMount() {
this._loadInitialState().done();
}
async _loadInitialState(){
try{
var value=await AsyncStorage.getItem(this.props.data.type);
if(value!=null){
this._saveActiveID(value);
}
} catch(error){
}
}
_saveActiveID(id) {
this.setState({activeID: id});
}

React-Rails Unexpected Token Error for any function other than render()

I am getting an unexpected token error whenever I add any function before render().
For example:
var App = React.createClass({
constructor() {
super();
this.state = {
notebooks: {}
};
}
render: function() {
return (
<div style={{"height": "100%"}}>
<NotebookNav notebooks={this.props.notebooks}></NotebookNav>
<Technique></Technique>
</div>
);
}
});
This component compiles fine when I delete the constructor() function, but with constructor(), it throws the unexpected token error pointing at render: function(). Anyone have any ideas on how to fix this?
You're confusing syntax here. createClass takes an object as an argument, not an ES6 class. Objects need commas separating items. Also, plain objects don't have a constructor like class does. In React's createClass object spec you probably want getInitialState instead
React.createClass({
getInitialState() {
return { notebooks: {} }
}, // <--- comma
render() {...}
})
you could however rewrite the entire thing using an ES6 class (which does not have commas separating methods.
class App extends React.Component {
constructor() {
super();
this.state = {
notebooks: {}
};
} // <--- no comma
render() {
return (
<div style={{"height": "100%"}}>
<NotebookNav notebooks={this.props.notebooks}></NotebookNav>
<Technique></Technique>
</div>
);
}
}
Yes, this is because you are not separating the function with a comma. You should write it like this
var App = React.createClass({
getInitialState: function() {
return {
notebooks: {}
};
},
render: function() {
return (
<div style={{"height": "100%"}}>
<NotebookNav notebooks={this.props.notebooks}></NotebookNav>
<Technique></Technique>
</div>
);
}
});
also should be this.state.notebooks not this.props.notebook

RelayMutation expects prop to be data fetched by Relay, adding mutation to query not working

I am getting the error:
bundle.js:28169 Warning: RelayMutation: Expected prop `group` supplied to `AddBlock` to be data fetched by Relay. This is likely an error unless you are purposely passing in mock data that conforms to the shape of this mutation's fragment.
It might seem similar to the problem described in this question, but the answer (of making sure the mutation is added to the initial query) is not working as a solution for me. I already have the mutation in the original query.
Here is my relevant code:
export class AddBlock extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation { addBlock }`;
}
getVariables() {
return {
body: this.props.body
};
}
getFatQuery() {
return Relay.QL`
fragment on AddBlock {
newBlockEdge,
group {
blocks
}
}
`;
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'group',
parentID: this.props.group.id,
connectionName: 'blocks',
edgeName: 'newBlockEdge',
rangeBehaviors: {
'': 'append',
},
}];
}
getOptimisticResponse() {
return {
newBlockEdge: {
node: {
body: this.props.body
}
},
group: {
id: this.props.group.id
}
}
}
static get fragments() {
return {
group: () => Relay.QL`
fragment on GroupNode {
id
}
`,
}
}
}
class Designer extends React.Component {
...
addToBlocks(blocks) {
// Create a mutation to save to the blocks.
Relay.Store.commitUpdate(
new AddBlock({
body: blocks[0].block,
group: this.props.group
})
);
}
...
}
Designer = Relay.createContainer(Designer, {
fragments: {
group: (Component) => Relay.QL`
fragment on GroupNode {
title
visibility
blocks(first: 20) {
edges {
node {
${Block.getFragment('block')}
${UpdateBlockBodyMutation.getFragment('block')}
position
title
}
}
}
${AddBlock.getFragment('group')}
}
`,
}
});
What could I be doing wrong here?
I suspect your mutation fragment isn't actually being used - you should run a test to see that if you add other fields to the AddBlock fragment, you'll find that they aren't being requested...? I'm not 100% sure why (likely something about static get fragments), but not quite sure.
Once you get your mutation fragment actually being used, Relay won't complain anymore since it'll be getting data in the correct way :D

Why doesn't solo_group call it's setUp method

I'm using scheduled_test to test my Polymer Dart elements. This works fine, until I try using solo_group(). My tests depend on the setUp() method being called, but when solo_group() is used, the setUp() method is not called. My tests, understandable fail, throwing errors about null values. Is there a reason for this? I tried using solo_test() instead, and this worked as I expected, calling the setUp() method, as it should, but not the solo_group.
I feel another bug report, but I want to confirm this is not the expected behaviour, before I do.
{UPDATE} As asked, here is an example, which isn't all the test code, but it should suffice. With this example, I expect the setUp() method to be called, but it isn't. However if I turn solo_group to just group, it does. setUp() also will be called if test() is replaced with solo_test(), and solo_group() is replaced with group().
class CheckedFieldComponent extends PageComponent {
CheckedFieldComponent(el) : super(el);
bool get value => component.model.value;
bool get checkIconShown => component.shadowRoot.querySelector('core-icon') != null;
}
void checked_field_test() {
CheckedFieldComponent component;
CheckedFieldComponent component2;
solo_group('[checked-field]', () {
setUp(() {
schedule(() => Polymer.onReady);
schedule(() {
BoolModel model = new Model.create('bool', '1', 'checked', true, true);
BoolModel model2 = new Model.create('bool', '2', 'checked', false, true);
PolymerElement element = createElement('<checked-field></checked-field>');
PolymerElement element2 = createElement('<checked-field></checked-field>');
element.model = model;
element2.model = model2;
document.body.append(element);
document.body.append(element2);
component = new CheckedFieldComponent(element);
component2 = new CheckedFieldComponent(element2);
return Future.wait([component.flush(), component2.flush()]);
});
currentSchedule.onComplete.schedule(() {
component.component.remove();
component2.component.remove();
});
});
test('model.value is true', () {
schedule(() {
expect(component.value, isTrue);
});
});
});
}

Resources