Why my test failed after adding didInsertElement to fetch json in ember? - javascript

I have a simple component that it's going to fetch data after the component is inserted. It was ok until I run my test. I got this error.
Assertion Failed: You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in a run
I understand that the component is fetching data asynchronously but I'm not sure how to solve that in my integration test.
Here's my code
export default Ember.Component.extend({
didInsertElement: function() {
let source = this.get('source');
let url = apiUrl + source;
Ember.$.getJSON(url).then(function(response) {
this.set('data', response.data);
}.bind(this));
},
// something else
};
And this is my test.
moduleForComponent('panel', 'Integration | Component | panel', {
integration: true,
beforeEach () {
this.render(hbs`{{panel}}`);
}
});
test('it has source dropdown', function(assert) {
assert.equal(this.$('select[name="Source"]').length, 1);
});
Without the fetching data bit, the test runs ok.

Try wrapping the getJSON inside an Ember run loop.
So something like this:
var self = this;
Ember.run(function(){
Ember.$.getJSON(url).then(function(response) {
self.set('data', response.data);
});
});

Create a new Promise and then set the response data in the success handler.
The promise returned by Em.$.getJSON and new Ember.RSVP.Promise are different.
let self = this;
let promise = new Ember.RSVP.Promise(function() {
return Ember.$.getJSON(url);
});
promise.then((json) => {
this.set('data', json);
});

Related

Vue prefetch data from separate backend

I have some queries from an API-Server that returns a json object that will be static over a user session, but not static forever.
It's a one-pager with Vue router.
How can I achieve that I:
can access this.myGlobals (or similar eg window.myGlobals) in all components, where my prefetched json-data from API-Server is stored.
My approach that is already working is to embed help.js via a mixin.
Oddly enough, I get hundreds of calls to this query. At first I thought that it only happened in the frontend and is chached, but the requests are actually sent hundreds of times to the server. I think it is a mistake of my thinking, or a systematic mistake.
i think the problem is, that the helper.js is not static living on the vue instance
main.js:
import helpers from './helpers'
Vue.mixin(helpers)
helpers.js:
export default {
data: function () {
return {
globals: {},
}
}, methods: {
//some global helper funktions
},
}, mounted() {
let url1 = window.datahost + "/myDataToStore"
this.$http.get(url1).then(response => {
console.log("call")
this.globals.myData = response.data
});
}
}
log in console:
call
SomeOtherStuff
(31) call
SomeOtherStuff
(2) call
....
log on server:
call
call
call (pew pew)
My next idea would be to learn vuex, but since its a easy problem, im not sure if i really need that bomb ?
You can use plugin to achieve this.
// my-plugin.js
export default {
install (Vue, options) {
// start fetching data right after install
let url1 = window.datahost + "/myDataToStore"
let myData
Vue.$http.get(url1).then(response => {
console.log("call")
myData = response.data
})
// inject via global mixin
Vue.mixin({
computed: {
myData () {
return myData
}
}
})
// or inject via instance property
Vue.prototype.$myData = myData
// or if you want to wait until myData is available
Vue.prototype.$myData = Vue.$http.get(url1)
.then(response => {
console.log("call")
myData = response.data
})
}
}
and use it:
Vue.use(VueResource)
Vue.use(myPlugin)

Why Don't I have 'this' context inside of a .then() in my Ember component

I'm fairly new to Ember.
This is the code for my component:
import Ember from 'ember';
export default Ember.Component.extend({
profiles: Ember.inject.service(),
tagName: 'td',
classNames: ['grey'],
classNameBindings: ['unreadMessages'],
unreadMessages: null,
onInit: function() {
const id = this.get('conversation.id');
return this.get('profiles').getMessages(id)
.then(function(bool) {
this.set('unreadMessage', bool);
});
}.on('init')
});
This throws:
TypeError: Cannot read property 'set' of undefined
So I can gather that I don't have the this context that I need to call this.set inside of the .then()
I need to assign the result of return this.get('profiles').getMessages(id) to the unreadMessages property in my component. So that I can use it for the classNameBinding.
Here is the method I'm calling from the service
getMessages(id){
return this.get('ajax').request('/messages?id=' + id)
.then((obj) => {
const unreadMessages = obj.messages.filter((e) => e.read === false);
if (unreadMessages === []) {
return false;
} else {
return true;
}
});
}
I've only been able to access the boolean value that getMessages returns inside of its .then() and I am not able to call this.set() inside of the .then() I'm looking for a work around. I think I'm close and am struggling to due to my lack of experience with Ember.
getMessages makes a 'GET' request to my back end and filters through the messages to check if there are any that are unread and then returns true or false. The purpose of the classNameBinding is to notify the user of whether they have any unread messages for that thread. This is a very simple email style messaging app that I am building for practice.
Thanks!
Change
onInit: function() {
const id = this.get('conversation.id');
return this.get('profiles').getMessages(id)
.then(function(bool) {
this.set('unreadMessage', bool);
});
}.on('init')
});
to
onInit: function() {
const id = this.get('conversation.id');
return this.get('profiles').getMessages(id)
.then((bool) => {
this.set('unreadMessage', bool);
});
}.on('init')
});
The thing here is, the scope changes when you write function(){} inside then and this won't refer to the component this.
That's why in ES6 the concept of lexical this was introduced. This will retain the this. So use Arrow function instead and it'll work smoothly..

React Component JSON Callback Scope

I'm using generator-react-webpack to create a React web app. This web app relies on JSON feeds - one of which is hosted on a CDN that does not support JSONP and the CDN url is a subdomain of the webapp. Is there any way to return the JSON data from within the React Component?
Basic React Component:
var AppComponent = React.createClass({
loadData: function() {
jQuery.getJSON(jsonFile.json?callback=?)
.done(function(data) {
console.log(data);
}.bind(this));
},
render: function(){
return ( ... );
}
});
I've tried a few solutions, and have come to the conclusion that I need to define my own callback on the JSON file like so:
JSON:
handleData({
"data": "hello World"
})
Is there a way for the handleData callback to be defined in the react component, or the response accessed from the react component? Any thoughts as to how I can get this to work are much appreciated. Thanks!
This looks like an odd way to do things, especially the part where you're using jQuery. That's a client-side utility to overcome not knowing where everything is and not having direct access to your elements. It makes no sense to use it when you're using React weith Webpack for bundling: React already knows where everything is (using refs) and Webpack means you can just use regular universal Node modules for everything that you need to do.
I'd recommend using something like, using request or a similar universal fetch API:
// loadData.js
var request = require('request');
var loadData = function(urlYouNeed, handler) {
request(urlYouNeed, function(error, response, body) {
if (error) {
return handler(error, false);
}
// do anything processing you need on the body,
var data = process(body);
handler(false, data);
};
So: just a module you can require in any component you define with require('./loadData'). And then in your actual component you do this:
var loadData = require('./loadData');
var AppComponent = React.createClass({
getDefaultProps: function() {
return {
jsonURL: "cdn://whateverjson.json"
};
},
getInitialState: function() {
loadData(this.props.jsonURL, this.updateData);
return {
data: []
}
},
updateData: function(err, data) {
if (err) {
return console.error(err);
}
data = secondaryEnsureRightFormat(data);
this.setState({ data: data });
},
render: function(){
var actualThings = this.state.data.map((entry, pos) => {
return <Whatever content={entry} key={entry.dontUseThePosVariableUpThere}/>
});
return (
<div>
...
{actualThings}
...
</div>
);
}
});
Much cleaner.
If I understand correctly the question, you only have to change your loadData this way :
loadData: function() {
var c = this
jQuery.getJSON(jsonFile.json?callback=?)
.done(function(data) {
c.handleData(data)
});
},
handleData: function(data) {
/* Implement here the function to handle the data */
},

Cannot get response content in mithril

I've been trying to make a request to a NodeJS API. For the client, I am using the Mithril framework. I used their first example to make the request and obtain data:
var Model = {
getAll: function() {
return m.request({method: "GET", url: "http://localhost:3000/store/all"});
}
};
var Component = {
controller: function() {
var stores = Model.getAll();
alert(stores); // The alert box shows exactly this: function (){return arguments.length&&(a=arguments[0]),a}
alert(stores()); // Alert box: undefined
},
view: function(controller) {
...
}
};
After running this I noticed through Chrome Developer Tools that the API is responding correctly with the following:
[{"name":"Mike"},{"name":"Zeza"}]
I can't find a way to obtain this data into the controller. They mentioned that using this method, the var may hold undefined until the request is completed, so I followed the next example by adding:
var stores = m.prop([]);
Before the model and changing the request to:
return m.request({method: "GET", url: "http://localhost:3000/store/all"}).then(stores);
I might be doing something wrong because I get the same result.
The objective is to get the data from the response and send it to the view to iterate.
Explanation:
m.request is a function, m.request.then() too, that is why "store" value is:
"function (){return arguments.length&&(a=arguments[0]),a}"
"stores()" is undefined, because you do an async ajax request, so you cannot get the result immediately, need to wait a bit. If you try to run "stores()" after some delay, your data will be there. That is why you basically need promises("then" feature). Function that is passed as a parameter of "then(param)" is executed when response is ready.
Working sample:
You can start playing with this sample, and implement what you need:
var Model = {
getAll: function() {
return m.request({method: "GET", url: "http://www.w3schools.com/angular/customers.php"});
}
};
var Component = {
controller: function() {
var records = Model.getAll();
return {
records: records
}
},
view: function(ctrl) {
return m("div", [
ctrl.records().records.map(function(record) {
return m("div", record.Name);
})
]);
}
};
m.mount(document.body, Component);
If you have more questions, feel free to ask here.

ember-cli data returned empty using initializer

I have an app where we need to create an initializer that inject our global into all the route where our global is a function that load data from a JSON file and return the data.
global-variable.js
export function initialize(container, application) {
var systemSetting = {
systemJSON: function(){
return Ember.$.getJSON("system/system.json").then(function(data){
return data
});
}.property()
};
application.register('systemSetting:main', systemSetting, {instantiate: false});
application.inject('route', 'systemSetting', 'systemSetting:main');
}
export default {
name: 'global-variable',
initialize: initialize
};
index.js - route
export default Ember.Route.extend({
activate: function(){
var _settings = self.systemSetting.systemJSON;
console.log(_settings.test);
},
}
system.JSON
{
"test" : 100
}
the result of the console.log give me this
ComputedProperty {isDescriptor: true, _dependentKeys: Array[0], _suspended: undefined, _meta: undefined, _cacheable: true…}
I think it's because of the JSON is not loaded yet but after that I try to do something like this at route
index.js - route
activate: function(){
var self = this;
var run = Ember.run
run.later(function() {
var _settings = self.systemSetting.systemJSON;
console.log(_settings);
}, 1000);
},
but still give me the same log. Am I use wrong approach to this problem?
I finally found the answer. Because of what I want to call is from an initializer then one that I must do is to use .get and if I just using get then the one that I received is a promise and to get the actual data I must use .then
The code will look like this:
index.js - route
activate: function(){
this.get('systemSetting.systemJSON').then(function(data) {
console.log(data.test);
});
}

Categories