I have an Ionic 2 Component. It renders a html page. In the constructor, it fetches data in a promise. The html uses the data (personModel) and displays the values.
My problem is the html wants to render before the promise has completed getting the data, resulting in an error.
TypeError: self.context.personModel is undefined
How do I make sure the html waits for the data to load before it tries to render?
Thanks
html
<h2>{{personModel.firstName}} {{personModel.lastName}}</h2>
ts
#Component({
templateUrl: 'build/pages/person/person.html',
})
constructor() {
// promise that loads data
this.utilityService.getLoggedInPerson().then((data: string) => {
this.personModel = JSON.parse(data);
}
}
personModel: any[];
constructor() {
this.personModel = [];
// your call
}
Try to initialize the object.
EDIT: answered together :P
If your service can return an Observable, the async pipe might help.
https://angular.io/docs/ts/latest/guide/pipes.html
You need to define the member variable straight away, and then assign the data to it, i.e:
personModel: any;
constructor() {
// promise that loads data
this.utilityService.getLoggedInPerson().then((data: string) => {
this.personModel = JSON.parse(data);
}
}
Related
I am new to Livewire and have a question.
In my Livewire blade I have a JS function that calls a component method which updates $this->questions.
function QuestionBatchRequest() {
Livewire.emit('moreQuestions');
this.dispatchEvent(new Event('moreQuestions'));
}
This works as I can output to an input box and see the data change.
I then have
document.addEventListener('livewire:load', () => {
window.livewire.on('QuestionBatchListener', () => {
let data = #entangle('questions');
myUnityInstance.SendMessage("JS-Unity", "InjectQuizData", data);
});
});
How can I get the updated $this->questions to my JS variable directly?
This is in my method in my component
$this->questions = json_encode($data, JSON_UNESCAPED_SLASHES);
$this->emit('QuestionBatchListener');
This function is called from my Unity game on the same page. The initial set of questions is received on page load and works fine.
OK, so I changed
let data = #entangle('questions');
with
let data = #this.get('questions');
...
Most of the information out there about Apollo Client and GraphQL queries is about fetching data and immediately rendering something.
What about the common use case where I want to fetch data to, let say, update the state in which I clearly don't need to render JSX, I just want to run Javascript code.
Use the following code snippet as an example
onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
console.info('variables ready', variables)
return (
<Query query={RESOLVE_REF} variables={variables}>
{ ({ data, error }) => {
console.info('data response', data)
console.info('error response', error)
return data
}}
</Query>
)
}
Apollo forces me to use the Query component just to perform a query, even when I don't want to render anything. Also those console.info never log anything, but the variables ready text does appear.
I have found that the documentation is pretty clear on using the Query component, but obscure on every option which is different. I feel I'm missing something.
I'm also concerned about how Apollo doesn't seems respect the separation of responsibilities, apparently merging both data and presentation into a single responsibility (as is clear with the Query component), which in my current understanding is quite silly, but most likely I'm fucking things up.
Any insight is appreciated.
As long as you've configured and included an ApolloProvider at the top of your component tree, you can get your query instance using either the withApollo HOC, or the ApolloConsumer:
const MyComponent = ({ client }) => {
// use it!
}
withApollo(MyComponent)
<ApolloConsumer>
{client => (
// use it!
)}
</ApolloConsumer>
You can then use any of the methods that are available to the client instance, including query and mutation, both of which return a Promise that resolves to an ApolloQueryResult object that includes data and errors. The full documentation for the client's API can be found here. Your code would then look something like:
async onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
try {
const { data } = await this.props.client(RESOLVE_REF, { variables })
} catch (e) {
// Handle errors
}
}
I'm using a custom test helper which requires access to the Ember data store, but I don't know how to access it from the given application argument.
export default registerAsyncHelper('myCustomHelper', function(app) {
console.log(app); // how to access store?
let store = app.__registry__.registrations['service:store'];
store.pushPayload(// json payload);
});
How can I get access to the store when registering a custom helper? I've been trying to figure out a way to access it from the __registry__.registrations['service:store'] key but that gives me an undefined value, when I can see that it's there and has the pushPayload function. Help would be greatly appreciated
Hah! I think I got it:
export default registerAsyncHelper('myCustomHelper', function(app) {
let instance = app.buildInstance();
let store = instance.lookup('service:store');
store.pushPayload(// json payload);
});
Not sure if that has any side effects though? Please let me know if it does, I think I've spent enough time trying to setup a good test environment already :p
This is typescript, but it should hopefully work the same in js (without the type annonations though)
// tests/helpers/get-service.ts
import { getContext } from "#ember/test-helpers";
export function getService<T>(name: string): T {
const { owner } = getContext();
const service = owner.lookup(`service:${name}`);
return service;
}
example usage:
// tests/helpers/create-current-user.ts
import { run } from '#ember/runloop';
import { DS } from 'ember-data';
import Identity from 'emberclear/data/models/identity/model';
import { getService } from './get-service';
export async function createCurrentUser(): Promise<Identity> {
const store = getService<DS.Store>('store');
const record = store.createRecord('identity', {
id: 'me', name: 'Test User'
});
await record.save();
return record;
}
this code is from https://emberclear.io
https://gitlab.com/NullVoxPopuli/emberclear/tree/master/packages/frontend/tests/helpers
hope this helps :)
I have a working Django REST API which returns this:
{
"id": 1,
"brand": "peugeot",
"model": "3008",
"variant": "allure"
}
I am using the following code to fetch the above data:
render() {
const { brand, model, variant } = this.props;
let url = `http://127.0.0.1:8000/api/car/${brand}/${model}/${variant}/`;
console.log(url) <== url is correct when checked in console
fetch(url)
.then(response => response.json())
.then(data => data.length === 0 ? this.setState({
data : data
}) : null ) <== I have used a condition for setState to stop fetching infintely
const { data } = this.state;
console.log(data) <== This is a blank object with no data in console
console.log(data.id) <== This is undefined in console
return (
<div>
{data.id} <== No data is shown on webpage
Car Details
</div>
);
}
No error is shown when I try to fetch the data on my webpage. What am I doing wrong?
P.S. Data can be fetched from the same API server when I have an array of objects, and I use map to loop over the data. Over here I am trying to fetch a single item so there is no array, just an object. Am I doing something wrong with the syntax?
You should never fetch or setState inside the render function.
render is called many times due to all kinds of side effects, i.e scrolling, clicking, props changing etc. This kind of code could cause all kinds of trouble.
If you need to perform the request once, call the fetch function inside componentDidMount. Also, I believe your callbacks should look something like this:
fetch(url)
.then(response => response.json())
.then(data => this.setState({ data : data }))
Taken from the docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
I changed the condition before 'setState' to JSON.stringify(data) !== JSON.stringify(this.state.data) and now it works.
should it be:
.then(data => data.length > 0 ? this.setState({ data }) : null )
I have a model built from a JSON object.
// extend the json model to get all props
App.Model = Ember.Object.extend(window.jsonModel);
I want to automatically save the model when anything is updated. Is there any way I can add an observer to the whole model?
EDIT: // adding the solution I currently go
For now I do:
// XXX Can't be right
for (var prop in window.jsonModel) {
if (window.jsonModel.hasOwnProperty(prop)) {
App.model.addObserver(prop, scheduleSave);
}
}
This is a large form, which means I'm adding tons of observers – it seems so inefficient.
A firebug breakpoint at Ember.sendEvent() reveals that there are events called App.model.lastName:change being sent. I could hack in an intercept there, but was hoping for an official way.
You can bind to isDirty property of subclass of DS.Model. The isDirty changes from false to true when one of model properties changes. It will not serve well for all cases because it changes only once until reset or committed, but for your case -
I want to automatically save the model when anything is updated. Is there any way I can add an observer to the whole model?
it may work fine.
From the article:
autosave: function(){
this.save();
}.observes('attributes'),
save: function(){
var self = this,
url = this.get('isNew') ? '/todos.json' : '/todos/'+this.get('id')+'.json',
method = this.get('isNew') ? 'POST' : 'PUT';
$.ajax(url, {
type: 'POST',
// _method is used by Rails to spoof HTTP methods not supported by all browsers
data: { todo: this.get('attributes'), _method: method },
// Sometimes Rails returns an empty string that blows up as JSON
dataType: 'text',
success: function(data, response) {
data = $.trim(data);
if (data) { data = JSON.parse(data); }
if (self.get('isNew')) { self.set('id', data['todo']['id']); }
}
});
},
isNew: function(){
return !this.get('id');
}.property('id').cacheable(),
I had the same requirement, and not finding a suitable answer, I implemented one.
Try this: https://gist.github.com/4279559
Essentially, the object you want to observe all the properties of MUST be a mixed of Ember.Stalkable. You can observe the properties of that object as 'item.#properties' (or, if you bake observers directly on the Stalkable, '#properties' alone works. "#ownProperties", "#initProperties" and "#prototypeProperties" also work, and refer to (properties that are unique to an instance and not defined on any prototype), (properties that are defined as part of the create() invocation), and (properties that are defined as part of the class definition).
In your observers, if you want to know what properties changed and invoked the handler, the property "modifiedProperties", an array, will be available with the names of the changed properties.
I created a virtual property _anyProperty that can be used as a dependent key:
import Ember from 'ember';
Ember.Object.reopen({
// Virtual property for dependencies on any property changing
_anyPropertyName: '_anyProperty',
_anyProperty: null,
propertyWillChange(keyName) {
if (keyName !== this._anyPropertyName) {
this._super(this._anyPropertyName);
}
return this._super(keyName);
},
propertyDidChange(keyName) {
if (keyName !== this._anyPropertyName) {
this._super(this._anyPropertyName);
}
return this._super(keyName);
}
});