I am making a request to a 3rd api through my backend. The api returns an array of events to my frontend and I am having trouble storing it in the ember DS. The route for my API request is eventful and the route/model I am trying to create an event for is eventful-event. I call:
this.get('store').createRecord('eventful-event', concert)
in my eventful adapter and get that 'store' is undefined. Here is my code for my eventful route:
import Ember from 'ember';
export default Ember.Route.extend({
model () {
return this.get('store');
},
data : {},
ajax: Ember.inject.service(),
actions: {
searchForEvents (data) {
let eventful = this.get('store').createRecord('eventful', data);
return eventful.save();
// .then(() => this.transitionTo('search-results'));
// return this.get('store').findAll('eventful');
// let something = this.get('store').findAll('eventful');
// console.log('something:', something);
// return eventful.save();
}
}
});
and my eventful adapter:
import ApplicationAdapter from 'ga-wdi-boston.event-bookmarker/application/adapter';
import Ember from 'ember';
export default ApplicationAdapter.extend({
ajax: Ember.inject.service(),
createRecord (store, type, record) {
let serialized = this.serialize(record, { includeId: true});
let data = { serialized };
let dataToSend = {'keywords': record.record.keywords, 'location': record.record.location };
return this.get('ajax').request('/eventful', {
method: 'POST',
data: dataToSend,
}).then((events) => {
events['eventful-event'].forEach(function(concert){
this.get('store').createRecord('eventful-event', concert);
});
});
}
});
This forEach is where the error is thrown. I'm new to ember so I apologize if I am overlooking something simple, or if my approach is not correct. Any advice or help would be much appreciated.
Just put a debugger in. 'This' in the adapter is undefined.
I believe context is your problem. Try using store (argument of adapter method) instead of this.get('store') inside local function:
createRecord (store, type, record) {
let serialized = this.serialize(record, { includeId: true});
let data = { serialized };
let dataToSend = {'keywords': record.record.keywords, 'location': record.record.location };
return this.get('ajax').request('/eventful', {
method: 'POST',
data: dataToSend,
}).then((events) => {
events['eventful-event'].forEach(function(concert){
store.createRecord('eventful-event', concert);
});
});
}
Related
I've followed some fairly basic tutorials to set up a basic Web Api service and a React TS app to consume this, but when my react component calls the WebApi service, I can see the Web Api gets stepped into and returns the data - as it does if I enter the API url in the browser, it returns the correct items JSON, but in the javascript code for React, the HTTP response doesn't appear to contain any data when the promise comes back from fetch, just an empty 200 response.
Ie response.data is undefined.
This must be something very basic I'm doing wrong - as like I mentioned when you enter the API url in the browser, you see the correct JSON in the browser. So why cant my react code understand the response?
My Web Api
[EnableCors("*", "*", "*")]
public class ItemsController : ApiController
{
private readonly Item[] _items = {
new Item
{
...
},
new Item
{
...
},
new Item
{
...
},
};
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, _items);
}
public HttpResponseMessage Get(long id)
{
var item= _items.FirstOrDefault(t => t.ItemId == id);
if (item== null)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Not found");
}
return Request.CreateResponse(HttpStatusCode.OK, item);
}
}
My react component
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import '../../App.css';
import IStoreState from '../../interfaces/IStoreState';
import Item from '../../model/item';
import { getItemsReceivedAction } from './actions';
interface IItemsProps {
items: Item[],
itemsUpdated: (items:Item[]) => void
}
class Trades extends React.Component<IItemsProps> {
constructor(props:IItemsProps) {
super(props);
}
public componentDidMount() {
fetch('http://localhost:58675/api/items', {
headers: {
Accept: 'application/json',
},
method: 'GET'
}).then((t:any) =>
{
const results = t;
this.props.itemsUpdated(results.data);
} );
}
public render() {
return (
<div>
{this.props.items} items displayed
</div>
);
}
}
const mapDispatchToProps = (dispatch:Dispatch) => {
return {
itemsUpdated: (items:Item[]) => dispatch(getItemsReceivedAction(items))
}
}
function mapStateToProps(state:IStoreState) {
return {
items: state.viewingItems
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Items);
Edit: javascript results object below
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:58675/api/items"
__proto__: Response
It sees to me that the problem is in the fetch part of you react component. What you get in the first then callback is a Response object.
In order to get the data from it, you need to call one of it's methods (which return a promise). In your case, since the response contains json, you need to call the json() method. After that, you chain another then where you manipulate the parsed data:
fetch('http://localhost:58675/api/items', {
headers: {
Accept: 'application/json',
},
method: 'GET'
}).then((t:any) =>
{
return t.json(); // <-- this part is missing
}
).then((t:any) =>
{
const results = t;
this.props.itemsUpdated(results.data);
}
)
I have a vuejs script and need to use an elasticsearch api method.
// ./main.js
var Vue = require('vue');
Vue.use(require('vue-resource'));
import ES from './elasticsearch.js';
new Vue({
el: 'body',
methods: {
search: function() {
// should call the es.search...
}
}
});
and the elasticsearch script:
// ./elasticsearch.js
var es = require('elasticsearch');
var client = new es.Client({
host: 'localhost:9200'
,log: 'trace'
});
client.search({
index: 'my_index',
type: 'my_type',
body: {
fields: {},
query: {
match: {
file_content: 'search_text'
}
}
}
}).then(function (resp) {
var hits = resp.hits.hits;
}, function (err) {
console.trace(err.message);
});
So, in the method search in main.js should call the client.search and send the text to be searched in my server (_search_text_).
How do we bind it? Or how do we use the elasticsearch object inside a vuejs method?
Thanks!
your elasticsearch.js file is not configured correctly as a module: import ES from './elasticsearch' won'T do anything because the file does not export anything.
it should probably look more like this:
// ./elasticsearch.js
var es = require('elasticsearch');
var client = new es.Client({
host: 'localhost:9200'
,log: 'trace'
});
function search (myIndex, myType, searchText)
return client.search({
index: myIndex,
type: myType,
body: {
fields: {},
query: {
match: {
file_content: searchText
}
}
}
}).then(function (resp) {
return hits = resp.hits.hits;
}, function (err) {
console.trace(err.message);
});
export { search }
We define a function named search and export it. Note That I also inxluded return statements to actually return the Promise and result from the function.
Then in main.js we can import it by that name, and use it:
// ./main.js
var Vue = require('vue');
Vue.use(require('vue-resource'));
import { search } from './elasticsearch.js';
new Vue({
el: 'body',
methods: {
search: function() {
var result = search('someindex', 'sometype', 'Search text here' ).then(function(res) {
// do something with the result
})
}
}
});
I suppose "resp.hits.hits" in your code is the search result JSON Object Array ,then you can define your vue instance like below:
// ./main.js
var Vue = require('vue');
Vue.use(require('vue-resource'));
import ES from './elasticsearch.js';
new Vue({
el: 'body',
data:{
searchResults:[] //this tell vue it is an array
}
methods: {
search: function() {
var self = this;
// call the search and get response data
client.search(function(resp){
self.searchResults = resp.hits.hits
})
}
}
});
and in your html,you just bind DOM to searchResults,that's all.
I have pass two callback function success and error on promise returned from ajax call using then method. Now i am unable to get Ember component object inside success/error method.
import Ember from 'ember';
export default Ember.Component.extend({
data:null,
issueType:'',
description:null,
prepareSubmitRaiseIssueModal:function(){
var data = this.get('data');
this.set('ticket.category',data.category);
this.set('ticket.name',this.get('session.currentUser.first_name'));
this.set('ticket.phone',this.get('session.currentUser.phone'));
this.set('ticket.groupId',data.groupId);
this.set('ticket.ownerId',this.get('session.currentUser.id'));
this.set('ticket.oyoId',this.get('session.currentOwnerHotelOyoId'));
this.set('ticket.ticketRaisedBy','owner');
this.set('ticket.bookingId',data.bookingId);
this.set('ticket.subType',data.subType);
this.set('ticket.subSubIssue',data.subSubIssue);
this.set('ticket.email',this.get('ticket.oyoId')+'#oyoproperties.com');
this.set('ticket.subject',this.get('ticket.oyoId')+' : '+this.get('ticket.category'));
this.set('ticket.description',this.get('description'));
},
success:function(){
console.log(this.get('description'));
},
error:function(){
console.log(this.get('description'));
},
actions :{
submitIssue:function(){
this.prepareSubmitRaiseIssueModal();
this.get('ticket').submitRaiseIssue().then(this.success,this.error);
//this.send('closeRaiseIssueModal');
},
closeRaiseIssueModal:function(){
this.sendAction('closeRaiseIssueModal');
}
}
});
i am able to get Ember component object if instead of passing named function i pass anonymous function.
submitIssue:function(){
var self = this;
this.prepareSubmitRaiseIssueModal();
this.get('ticket').submitRaiseIssue().then(function(response){
console.log(self.get('description'));
},
function(err){
console.log(self.get('description'));
});
//this.send('closeRaiseIssueModal');
},
is there any way i can get the Ember component object's reference for former case??
Wow speaking of a spaghetti.
prepareSubmitRaiseIssueModal:function(){
var data = this.get('data');
this.set('ticket.category',data.category);
this.set('ticket.name',this.get('session.currentUser.first_name'));
this.set('ticket.phone',this.get('session.currentUser.phone'));
this.set('ticket.groupId',data.groupId);
this.set('ticket.ownerId',this.get('session.currentUser.id'));
this.set('ticket.oyoId',this.get('session.currentOwnerHotelOyoId'));
this.set('ticket.ticketRaisedBy','owner');
this.set('ticket.bookingId',data.bookingId);
this.set('ticket.subType',data.subType);
this.set('ticket.subSubIssue',data.subSubIssue);
this.set('ticket.email',this.get('ticket.oyoId')+'#oyoproperties.com');
this.set('ticket.subject',this.get('ticket.oyoId')+' : '+this.get('ticket.category'));
this.set('ticket.description',this.get('description'));
},
How about
prepareSubmitRaiseIssueModal:function(){
var data = this.get('data');
var ticket = this.get('ticket')
ticket.setProperties({
'category': data.category,
'name': ...
})
},
And to pass reference's you can either use
promise.then(function() {
this.mysuccess();
}.bind(this), function() {
this.myerror();
}.bind(this))
const self = this;
promise.then(function() {
self.blah();
});
promise.then(result => {
this.blah();
})
In your case I would write a utility JS file for displaying notifications.
And handle success for each promise personally and let errors be handled in a general error method.
utils/notifications.js
function reportError(error) {
displayNotification('error', getErrorMessage(error));
}
import reportError from 'utils/notifications';
export default Ember.Controller.extend({
....
promise.then(result => {
// Do custom stuff with result;
}, reportError);
....
});
And promise within a promise
return promise1.then(x => {
return promise2.then(x2 => {
return promise3 ... etc
})
}).catch(reportError); // Single hook needed but u need to return promises
I am having some issues with Ember.js and loading resources asynchronously (aka I don't know what I'm doing). This is what I have. It currently doesn't update the list parameter after receiving the data. The template does not render anything. Thanks for any help.
Utilities
import Ember from 'ember';
export var page = Ember.Object.extend({
type: null,
list: Ember.computed('type', function() {
var type = this.get('type');
var url = "/test/" + type + "/test2";
if (type) {
getArray(url).then(function(list) {
return list;
});
} else {
return [];
}
})
});
export function get(url) {
return Ember.$.ajax({
url: url,
type: 'GET',
dataType: 'text',
cache: true,
xhrFields: {
withCredentials: true
}
});
}
export function getArray(url) {
return get(url).then(
function(file) {
var array = file.split("\n");
array.pop();
return array;
},
function() {
return ["Error!"];
}
);
}
Route
import util from 'app/utils/utilities';
export default Ember.Route.extend({
model(params) {
var p = util.page.create({
type: params.log_type
});
return p;
}
});
Template
{{#each model.list as |item|}}
<li>{{item}}</li>
{{/each}}
Consider the following part of your code:
if (type) {
getArray(url).then(function(list) {
return list;
});
} else {
return [];
}
This is not going to do what you think it does. return list returns list as the value of the promise, but you are not then doing anything with that promise. In particular, be clear that the computed property list will not take on that value. When the if (type) branch is taken, the computed property list will have the value undefined (since it's not returning anything in that case).
Remember that model wants you to return a promise (at least, if you want it to do its thing, which is to wait for the promise to resolve, then proceed with the transition, then use the resolved value of the promise to call afterModel and setupController etc.) Therefore, instead of making list a computed property, make it a regular method which returns a promise for the model hook on your route to consume:
import Ember from 'ember';
export var page = Ember.Object.extend({
type: null,
list: function() {
var type = this.get('type');
var url = "/test/" + type + "/test2";
if (!type) return [];
return getArray(url);
});
Then in your route
import util from 'app/utils/utilities';
export default Ember.Route.extend({
model(params) {
var p = util.page.create({
type: params.log_type
});
return p.list();
}
});
Your model will then be the list, so in the template:
{{#each model as |item|}}
<li>{{item}}</li>
{{/each}}
I have something like:
App.IndexController = Ember.ObjectController.extend({
results : function(){
return new Ember.RSVP.Promise(function(resolve, reject){
Ember.$.getJSON('/search').then(function(res){
console.log('response data is: ', res);
resolve(res)
})
})
}.property(),
...
})
// data
$.mockjax({
url: '/search',
responseText : {
type: 'different people',
res: [
{name: 'charlie', age: '55'},
{name: 'bobby', age: '19'},
{name: 'raymond', age: '39'}
]
}
})
my jsbin
How do I actually use the returned data? Currently, results returns a promise ( this.get('results') ) so I can't use it in my hbs template. Do I need to convert it to an object and then return that object?
Answering the Question
Internally Ember uses a PromiseProxyMixin to do the magic rendering of promises that we know and love. Here is your updated JSBin working with a PromiseProxy:
http://emberjs.jsbin.com/danazu/edit?html,js,output
Your results property becomes this:
results: Ember.computed.promise(function(resolve, reject) {
Ember.$.getJSON('/search').then(function(res) {
console.log('response data is: ', res);
return resolve(res);
});
})
However I don't recommend this. Take a look at this discourse thread to get some information on why you might not want to do this. In short, it will be clunky to handle all the different states of promises.
Looking For a Better Way
The Router is the perfect place to deal with promises with AJAX requests. There must be some reason that you're not just loading this data in your Router in the model or afterModel hooks. Could you create another nested resource to represent these search results and then just link-to that resource?
In your Router:
Router.map(function() {
this.resource('search', { path: '/search/:term' });
});
App.PeopleRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/search?term=' + params.term);
}
});
If that won't work at the very least you could:
Send an action when you want the search results
Handle the AJAX request in the controller
Set the results on the controller when they are resolved.
I use ic ajax for easy use with promises. I don't currently use Ember data so I wrote myself a nice rest client on top of ic ajax to easily get/post/put to urls:
rsvpAjax: function(url,method, data){
var headers = {};
// build request
var opts = {};
var defaultOpts = {
type: method,
headers: headers,
data: JSON.stringify(data),
dataType: 'json',
xhrFields: {
withCredentials: true
}
};
return ajax.request(url, $.extend({}, defaultOpts, opts));
}
And then I have get method:
getAsync: function(url){
return this.rsvpAjax(url, "GET");
}
Then using it elsewhere in a facade/helper class:
import objFactory from 'appname/models/obj';
...
...
foo: function(){
//get your rest client somehow
var apiFacade = this.getRestClient();
return restClient.getAsync('url_string').then(function(response){
//targets some part of the response - here its an array of
var data = response.data;
return data.map(function(obj){
return objFactory.create(obj);
});
});
}
foo returns an array of Ember objects. the objFactory I import is just a Ember.Object class. The obj passed into the create generally is a one to one match with the properties in said Ember.Object. With an initializer, I inject this object into every route:
import someFacade from "app/facades/facade";
export default {
name: 'someFacade',
initialize: function(container, app) {
//a singleton by default
container.register('facades:someFacade',someFacade);
app.inject('route', 'someFacade', 'facades:someFacade');
}
};
Any of my routes can use this facade without importing it thanks to DI.
//Ember blocks
model: function(){
return this.someFacade.foo();
}
or:
setupController(controller, model){
//set controller prop on return of resolved val
this.someFacade.foo().then(function(foosReslovedVal){
controller.set('someProp', foosResolvedVal);
});
}