I have a Component that has an injected service, which makes an Ajax call. I can receive the JSON data successfully and can dump it into the console after the promise "THEN" returns.
Here's my component. I can see the dumped data, but how do I set the component properties with that JSON and have it accessible in the template? Also, why can't I use "this.get" in my function below?
import Ember from 'ember';
export default Ember.Component.extend({
attr_types: Ember.inject.service('svc-attrtypes'),
atype_list: [],
actions: {
getATypes: function() {
this.get('attr_types').getTypes().then(function(json){
console.log(json);
this.atype_list = json;
console.log(this.atype_list);
// below returns: TypeError: this.get is not a function
this.get('atype_list').pushObjects(json);
});
}
}
});
In my template I have this:
{{#each atype_list.alltypes as |a|}}
<li>{{a.attr_type}} - {{a.attr_type_desc}}</li>
{{/each}}
If I manually place my JSON into the atype_list it shows perfectly on the template. But if I try to set it after my Ajax returns, nothing shows, except for in the console output.
I appreciate any help. I am sure I a missing something simple. ( or more likely, I'm just going about this all wrong)
This changed with anonymous function passed to then. You have to save this or use es6 arrow function syntax.
import Ember from 'ember';
const { service } = Ember.inject;
export default Ember.Component.extend({
attrTypes: service('svc-attrtypes'),
atypeList: [],
actions: {
// es6 version
getATypes(){
this.get('attrTypes').getTypes().then(array => {
this.set('atypeList', array); //replaces original array
this.get('atypeList').pushObjects(array); // adds array's elements to the end
});
}
// es5 version
getATypes: function () {
var _this = this;
this.get('attrTypes').getTypes().then(function(array){
_this.set('atypeList', array);
}
}
}
});
You wrote that you are new to ember, so I added little more syntax sugar. Also check ember-cli if you don't know about that already.
Related
I want the code below to be able to fetch JSON data from a simple API I have set up.
export class FaqFakeDb
{
public static data = fetch('https://#######.co.uk/api/faq/');
}
This is for a FAQ page and its meant to be pulling data from the API. I am new to typescript and Angular so forgive me if I've made a simple mistake.
I want it to function like this:
export class FaqFakeDb
{
public static data = [
{
"id":"1",
"question":"test1",
"answer":"test1"
},
{
"id":"2",
"question":"test1",
"answer":"test1"
},
{
"id":"3",
"question":"test1",
"answer":"test1"
}
];
}
Any help will me much appretiated :)
fetch returns a promise, so data will be a promise. Any time you want to access data you will have to use async/await or .then or something else to handle promises. You also don't have any error handling in case your API call fails.
One example of how you could handle this would be:
async ngOnInit() {
try {
const response = await FaqFakeDb.data;
this.quesitons = await response.json();
} catch {
// fetch encountered an error; you can handle it here.
}
}
However, I would recommend that you use Angular's built in HttpClientModule instead and make the API call as it's needed (in the component that needs it) rather than when an arbitrary class is declared.
When using a generic modal or toast with a confirm button, it becomes useful to be able to pass an action into this component so it can be dispatched when you click confirm.
The action may look something like this:
export function showConfirm({modalConfirm}) {
return {
type: 'MODALS/SHOW_MODAL',
payload: {
modalId: getUuid(),
modalType: 'CONFIRM',
modalConfirm : modalConfirm,
},
};
}
Where modalConfirm is another action object such as:
const modalConfirm = {
type: 'MAKE_SOME_CHANGES_AFTER_CONFIRM',
payload: {}
}
The modalConfirm action is dispatched inside the modal component using dispatch(modalConfirm) or even dispatch(Object.assign({}, modalConfirm, someResultFromTheModal)
Unfortunatley this solution only works if modalConfirm is a simple redux action object. This system is clearly very limited. Is there anyway you can pass a function (such as a thunk) in instead of a simple object?
Ideally, something full featured likes this:
const modalConfirm = (someResultFromTheModal) => {
return (dispatch, getState){
dispatch({
type: 'MAKE_SOME_UPDATES',
payload: someResultFromTheModal
})
dispatch({
type: 'SAVE_SOME_STUFF',
payload: http({
method: 'POST',
url: 'api/v1/save',
data: getState().stuffToSave
})
})
}
}
Funny, putting an action object in the store and passing it as a prop to a generic dialog is exactly the approach I came up with myself. I've actually got a blog post waiting to be published describing that idea.
The answer to your question is "Yes, but....". Per the Redux FAQ at http://redux.js.org/docs/FAQ.html#organizing-state-non-serializable , it's entirely possible to put non-serializable values such as functions into your actions and the store. However, that generally causes time-travel debugging to not work as expected. If that's not a concern for you, then go right ahead.
Another option would be to break your modal confirmation into two parts. Have the initial modal confirmation still be a plain action object, but use a middleware to watch for that being dispatched, and do the additional work from there. This is a good use case for Redux-Saga.
I ended up using string aliases to an actions library that centrally registers the actions.
Modal emmiter action contains an object with functionAlias and functionInputs
export function confirmDeleteProject({projectId}) {
return ModalActions.showConfirm({
message: 'Deleting a project it permanent. You will not be able to undo this.',
modalConfirm: {
functionAlias: 'ProjectActions.deleteProject',
functionInputs: { projectId }
}
})
}
Where 'ProjectActions.deleteProject' is the alias for any type of complicated action such as:
export function deleteProject({projectId}) {
return (dispatch)=>{
dispatch({
type: 'PROJECTS/DELETE_PROJECT',
payload: http({
method: 'DELETE',
url: `http://localhost:3000/api/v1/projects/${projectId}`,
}).then((response)=>{
dispatch(push(`/`))
}),
meta: {
projectId
}
});
}
}
The functions are registered in a library module as follows:
import * as ProjectActions from '../../actions/projects.js';
const library = {
ProjectActions: ProjectActions,
}
export const addModule = (moduleName, functions) => {
library[moduleName] = functions
}
export const getFunction = (path) => {
const [moduleName, functionName] = path.split('.');
// We are getting the module only
if(!functionName){
if(library[moduleName]){
return library[moduleName]
}
else{
console.error(`Module: ${moduleName} could not be found.`);
}
}
// We are getting a function
else{
if(library[moduleName] && library[moduleName][functionName]){
return library[moduleName][functionName]
}
else{
console.error(`Function: ${moduleName}.${functionName} could not be found.`);
}
}
}
The modalConfirm object is passed in to the modal by props. The modal component requires the getFunction function in the module above. The modalConfirm object is transformed into a function as follows:
const modalConfirmFunction = (extendObject, modalConfirm) => {
const functionFromAlias = getFunction(modalConfirm.functionAlias);
if(functionFromAlias){
dispatch(functionFromAlias(Object.assign({}, modalConfirm.functionInputs, extendObject)));
}
}
As you can see, this function can take in inputs from the modal. It can execute any type of complicated action or thunk. This system does not break time-travel but the centralized library is a bit of a drawback.
So I am making a rest call to get this JSON Object:
{
"_id":"57a0811276e75ba815d248b0",
"gName":"demo-layout",
"gType":"Content",
"wsId":"57a036c376e75ba815d248ac",
"desc":"Demo-Layout for rapidpage",
"createdDate":"2016-08-02T11:16:34.223Z",
"__v":0
}
Now I want to add an array to this object ,something like this:
{
"_id":"57a0811276e75ba815d248b0",
"gName":"demo-layout",
"gType":"Content",
"wsId":"57a036c376e75ba815d248ac",
"desc":"Demo-Layout for rapidpage",
"createdDate":"2016-08-02T11:16:34.223Z",
"blocks":[], //should be added at runtime
"__v":0
}
So I tried following:
dbPage:any={};
ngOnInit(){
let pageId:string="57a0811276e75ba815d248b0";
this._pagesService.getPageById(pageId).subscribe((Page)=>{
this.dbPage=rapidPage;
console.log(this.dbPage); //this prints the object as shown above
});
this.dbPage.blocks=[];
this.dbPage.blocks.push(block1);
}
But its not modifying the current object,instead its creating new Object as :
{blocks: Array[]}
any inputs?
That's because you're not assigning it in the subscribe call. Due to the async nature of HTTP requests in JavaScript, the code below the subscribe call will be executed before the callback inside the subscribe call.
You can easily fix this by moving the code inside the callback:
dbPage: any = {};
ngOnInit(){
let pageId: string = "57a0811276e75ba815d248b0";
this._pagesService.getPageById(pageId).subscribe((rapidPage) => {
this.dbPage = rapidPage;
console.log(this.dbPage); //this prints the object as shown above
this.dbPage.blocks = [];
this.dbPage.blocks.push(block1);
});
}
I know this has something to do with using $q and promises, but I've been at it for hours and still can't quite figure out how it's supposed to work with my example.
I have a .json file with the data I want. I have a list of people with id's. I want to have a service or factory I can query with a parameter that'll http.get a json file I have, filter it based on the param, then send it back to my controller.
angular
.module("mainApp")
.controller('personInfoCtrl',['$scope', '$stateParams', 'GetPersonData', function($scope, $stateParams, GetPersonData) {
$scope.personId = $stateParams.id; //this part work great
$scope.fullObject = GetPersonData($stateParams.id);
//I'm having trouble getting ^^^ to work.
//I'm able to do
//GetPersonData($stateParams.id).success(function(data)
// { $scope.fullObject = data; });
//and I can filter it inside of that object, but I want to filter it in the factory/service
}]);
Inside my main.js I have
//angular.module(...
//..a bunch of urlrouterprovider and stateprovider stuff that works
//
}]).service('GetPersonData', ['$http', function($http)
{
return function(id) {
return $http.get('./data/people.json').then(function(res) {
//I know the problem lies in it not 'waiting' for the data to get back
//before it returns an empty json (or empty something or other)
return res.data.filter(function(el) { return el.id == id)
});
}
}]);
The syntax of the filtering and everything works great when it's all in the controller, but I want to use the same code in several controls, so I'm trying to break it out to a service (or factory, I just want the controllers to be 'clean' looking).
I'm really wanting to be able to inject "GetPersonData" to a controller, then call GetPersonData(personId) to get back the json
You seems to be syntax issue in your filter function in the service.
.service('GetPersonData', ['$http', function($http){
return function(id) {
return $http.get('./data/people.json').then( function (res) {
return res.data.filter(function(el) { return el.id == id });
});
}}]);
But regarding the original issue you cannot really access the success property of the $q promise that you are returning from your function because there is no such property exist, It exists only on the promise directly returned by the http function. So you just need to use the then to chain it through in your controller.
GetPersonData($stateParams.id).then(function(data){ $scope.fullObject = data; });
If you were to return return $http.get('./data/people.json') from your service then you will see the http's custom promise methods success and error.
I want write two services one with a $http.get method and one with $resource
This service should receive a Json Object and looks like this, at the moment this code is direct in my controller and not in a service:
var csvPromise= $http.get(base_url + 'DataSource/1').success(function(data) {
$scope.data4=JSON.stringify(data);
});
The problem is, I want save received data in $scope.data4 and I want use this data after the $http.get call but the value is empty.
Direct after this call there is and Object that needs this value:
new myObject($scope.data4)
so myObject must wait so long until the data has arrived.
or can I make a synchronous call with $http or $resource ?
How can i do this ? I have found so many examples with promise and .then but nothing has worked for me.
EDIT: I have now written a service but it didn`t work:
var test=angular.module('myApp.getCSV', ['ngResource']);
test.factory('getCSV',function($log, $http,$q, $resource){
return {
getData: function (id) {
var csvPromise= $http.get(base_url +'DataSource/'+id)
.success(function(data) {
return data;
});
return csvPromise;
}
}
});
and then in my controller I call this:
getCSV.getData(1).then(function(theData){
$scope.data4=JSON.stringify(theData);
new myObject( $scope.data4); });
but this did not work. I thought if the $http.get receives the data then the then Function is called.
I don't believe you can do synchronous calls. That said, you have at least two options:
1) Pass in the data using the $routeProvider resolve feature. From the documentation:
An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected
An example on how to use this:
$routeProvider
.when('/your/path', {
templateUrl: '/app/yourtemplate.html',
controller: 'yourController',
resolve: {
data: ['$route', '$http', function($route, $http) {
return $http.get(base_url +'DataSource/1');
}]
}
})
And then in your controller:
app.controller('yourController', ['$scope', 'data', function($scope, data) {
$scope.data4 = JSON.stringufy(data);
var yourObj = new myObject($scope.data4);
}]);
2) The second option is to use promises and only instantiate your new myObject($scope.data4) once the promise successfully completes.
Your code needs to be changed just a bit:
$scope.data4 = '';
var csvPromise= $http.get(base_url +'DataSource/1');
csvPromise.then(function(data){
$scope.data4 = JSON.stringify(data);
}, function(data){
//error handling should go here
window.alert(data);
});
This should give you what it sounds to me like you need.
As i know, there's no way to sync~ call the http or resource. They're hard coded on AngularJS core file :
xhr.open(method, url, true);
And you don't want to hurt your users too by blocking the browser wait the data arrived. You'll better show how you make the nothing has worked for me so we can start working to fix it.
Have you try call new myObject($scope.data4) inside success method?
$http.get(...).success(function(data){
$scope.data4 = JSON.stringify(data); // I've no idea why do you need this.
var stuff = new myObject($scope.data4); // THis is now your.
});