$scope push is not a function - javascript

I have some code like:
$scope.offer = [];
function offer (Details) {
angular.forEach(Details, function (product) {
$scope.offer = SomeAPI.ById.query({ product: id }, function (response) {
$scope.offer.push(response);
});
});
console.log($scope.offer);
}
Error in consoleļ¼š$scope.offer.push is not a function.

Initialize the array before you push the value,
$scope.offer = [];
angular.forEach(Details, function (product) {
$scope.offer = SomeAPI.ById.query({ product: id }, function (response) {
$scope.offer.push(response);
});
});

Why are you assigning '$scope.offer' to return type of API call.
$scope.offer = SomeAPI.ById.query({ product: id }, function (response) {
This changes the type of '$scope.offer' from '[]' array type to probably a promise object which that api is returning. Thats why push method is not working for you.
Correct code should be:
$scope.offer = [];
function offer (Details) {
angular.forEach(Details, function (product) {
SomeAPI.ById.query({ product: id }, function (response) {
$scope.offer.push(response);
});
});
console.log($scope.offer);
}

Related

Load AJAX collections in order in JavaScript

How to ensure, in JavaScript (jquery) that some actions are performed one after other, in an order.
Say, I need to load schools collection BEFORE loading teachers, in order to assing the myTeacher.SchoolName = schools[myTeacher.SchoolId].name;
The pseudo code bellow:
const studentsUrl='api/students', teachersUrl='api/teachers', schoolsUrl='api/schools';
let students = null, teachers = null, schools = null;
$(document).ready(function () {
getSchools();
getTeachers();
getStudents();
});
function getSchools() {
$.get(schoolsUrl, function (data) {
window.schools = data;
});
}
function getTeachers() {
$.get(teachersUrl, function (data) {
window.teachers = data;
// >>> SHOULD BE SURE, SCHOOLS already loaded!!!
$.each(teachers, function (key, item) {
item.school = schools[item.schoolId].name;
});
});
}
function getStudents() {
$.get(studentsUrl, function (data) {
window.students = data;
// >>> SHOULD BE SURE, TEACEHRS already loaded!!!
$.each(students, function (key, item) {
item.teacher = teachers[item.teacherId].name;
});
});
}
PS.
Is there another way to assure order but the encapsulation of one function at the end of another?
As others already suggested you can chain requests.
I made few changes to your code.
Added Strict Mode it helps to prevent bugs
The code wrapped in IFFE in order to prevent global pollution
If all apis belong to the same server you can process all this data on server side
and return one filled json.
in this way your server will do a little extra work on constructing this json but in other hand you will make only one ajax request instead of 3.
This will work faster and you can cache this json for some time
Code for the first solution
(function () {
'use strict';
const studentsUrl = 'api/students';
const teachersUrl = 'api/teachers';
const schoolsUrl = 'api/schools';
let students = null;
let teachers = null;
let schools = null;
var scoolData = {
schools: null,
teachers: null,
students: null
};
$(document).ready(function () {
getSchools().then(function (schools) {
scoolData.schools = schools;
getTeachers().then(function (teachers) {
scoolData.teachers = teachers;
$.each(scoolData.teachers, function (key, item) {
item.school = scoolData.schools[item.schoolId].name;
});
});
});
});
function getSchools() {
return $.get(schoolsUrl);
}
function getTeachers() {
return $.get(teachersUrl,
function (result) {
scoolData.teachers = result;
// >>> SHOULD BE SURE, SCHOOLS already loaded!!!
$.each(teachers, function (key, item) {
item.school = scoolData.schools[item.schoolId].name;
});
});
}
})();
Since you only need all the results available and each request does not depend on the previous you can use jQuery.when
let students = null;
let teachers = null;
let schools = null;
$(document).ready(function() {
$.when(
getSchools(),
getTeachers()
).done(function(shoolResults, teacherResults) {
window.schools = shoolResults;
window.teachers = teacherResults;
handleTeachers();
getStudents();
});
function getSchools() {
return $.ajax({
type: 'GET',
url: schoolsUrl
});
}
function getTeachers() {
return $.ajax({
type: 'GET',
url: teachersUrl
});
}
function handleTeachers() {
$.each(teachers, function (key, item) {
item.school = schools[item.schoolId].name;
});
}
});
If you want them in order (though I'm not sure I understand why, since you retrieve all schools/teachers/students anyway), you can simply do this.
Note: get* functions are dummies in the following sample. Instead, just return the result of $.get calls from them:
function getSchools() {
return Promise.resolve({1: {name: 'school1'}});
}
function getTeachers() {
return Promise.resolve({1: {name: 'teacher1', schoolId: 1}});
}
function getStudents() {
return Promise.resolve({1: {name: 'student1', teacherId: 1}});
}
(async () => {
const schools = await getSchools();
const teachers = await getTeachers();
const students = await getStudents();
// Alternative for the $.each code
Object.values(teachers).forEach(teacher => teacher.school = schools[teacher.schoolId].name);
Object.values(students).forEach(student => student.teacher = teachers[student.teacherId].name);
console.log(schools, teachers, students);
})();
Another note: this is ES8 code, I'll post a non async/await version if you need to support older browsers and can't use a transpiler like Babel.
Non ES8-dependent code:
function getSchools() {
return Promise.resolve({1: {name: 'school1'}});
}
function getTeachers() {
return Promise.resolve({1: {name: 'teacher1', schoolId: 1}});
}
function getStudents() {
return Promise.resolve({1: {name: 'student1', teacherId: 1}});
}
let schools = null, teachers = null, students = null;
getSchools().then(_schools => {
schools = _schools;
return getTeachers();
}).then(_teachers => {
teachers = _teachers;
return getStudents();
}).then(_students => {
students = _students;
for (var _ in teachers) {
teachers[_].school = schools[teachers[_].schoolId].name;
}
for (var _ in students) {
students[_].teacher = teachers[students[_].teacherId].name
}
console.log(schools, teachers, students);
});
Call getTeachers(); when getSchools(); return success or complete, success preferred since complete runs if there's an error..
I think you are looking for this one.
getSchools().done(function(data){
var someId = data.findThatId;
getTeachers(someId);
});
You will need to return data from ajax call to get data in done.
You may load them asynchronously but you have to wait until both calls are finished.
To achieve this, add return before your ajax calls and combine the results in your ready function (not in the success handler of the teachers call):
let schoolsPromise = getSchools();
let teachersPromise = getTeachers();
$.when(schoolsPromise, teachersPromise)
.then((schools, teachers) => {
$.each(teachers, (key, item) => {
item.school = schools[item.schoolId].name;
});
});

Cannot access data from component method

I tried components methods in vue js. My code like this.
const Thread = Vue.component('threadpage', function(resolve) {
$.get('templates/thread.html').done(function(template) {
resolve({
template: template,
data: function() {
return {
data: {
title: "Data Table",
count: this.GetData
}
};
},
methods: {
GetData: function() {
var data = {
username : "newshubid",
data : {
page : 0,
length : 10,
schedule : "desc"
}
};
var args = {"data" : JSON.stringify(data)};
var params = $.param(args);
var url = "http://example-url";
var result;
DoXhr(url, params, function(response){
result = JSON.parse(response).data;
console.log("load 1", result);
});
setTimeout(function () {
console.log("load 2", result);
return result;
}, 1000);
}
},
created: function(){
this.GetData();
}
});
});
});
But, when I trying to use {{ data.count }} in template. Not showing result what i want. Even I tried return result in GetData.
Whats my problem ? And how to access data from methods ? Please help me, i'm a beginner. Thanks
See the edited code and comments I added below.
You tried to return the result by using return in the function from setTimeout, which won't help you return value from GetData.
Instead, You can just set the value in the callback function of your ajax request.
const Thread = Vue.component('threadpage', function(resolve) {
$.get('templates/thread.html').done(function(template) {
resolve({
template: template,
data: function() {
return {
data: {
title: "Data Table",
// NOTE just set an init value to count, it will be refreshed when the function in "created" invoked.
count: /* this.GetData */ {}
}
};
},
methods: {
GetData: function() {
var data = {
username : "newshubid",
data : {
page : 0,
length : 10,
schedule : "desc"
}
};
var args = {"data" : JSON.stringify(data)};
var params = $.param(args);
var url = "http://example-url";
var result;
var vm = this;
DoXhr(url, params, function(response){
result = JSON.parse(response).data;
// NOTE set data.count to responsed result in callback function directly.
vm.data.count = result;
});
// NOTE I think you don't need code below anymore.
// setTimeout(function () {
// console.log("load 2", result);
// return result;
// }, 1000);
}
},
created: function(){
this.GetData();
}
});
});
});

Set object in data from a method in VUE.js

I have been stuck with this issues for 2 hours now and I really can't seem to get it work.
const app = new Vue({
el: '#book-search',
data: {
searchInput: 'a',
books: {},
},
methods: {
foo: function () {
axios.get('https://www.googleapis.com/books/v1/volumes', {
params: {
q: this.searchInput
}
})
.then(function (response) {
var items = response.data.items
for (i = 0; i < items.length; i++) {
var item = items[i].volumeInfo;
Vue.set(this.books[i], 'title', item.title);
}
})
.catch(function (error) {
console.log(error);
});
}
}
});
When I initiate search and the API call I want the values to be passed to data so the final structure looks similar to the one below.
data: {
searchInput: '',
books: {
"0": {
title: "Book 1"
},
"1": {
title: "Book 2"
}
},
Currently I get Cannot read property '0' of undefined.
Problem lies here:
Vue.set(this.books[i], 'title', item.title);
You are inside the callback context and the value of this is not the Vue object as you might expect it to be. One way to solve this is to save the value of this beforehand and use it in the callback function.
Also instead of using Vue.set(), try updating the books object directly.
const app = new Vue({
el: '#book-search',
data: {
searchInput: 'a',
books: {},
},
methods: {
foo: function () {
var self = this;
//--^^^^^^^^^^^^ Save this
axios.get('https://www.googleapis.com/books/v1/volumes', {
params: {
q: self.searchInput
//-^^^^--- use self instead of this
}
})
.then(function (response) {
var items = response.data.items
var books = {};
for (i = 0; i < items.length; i++) {
var item = items[i].volumeInfo;
books[i] = { 'title' : item.title };
}
self.books = books;
})
.catch(function (error) {
console.log(error);
});
}
}
});
Or if you want to use Vue.set() then use this:
Vue.set(self.books, i, {
'title': item.title
});
Hope this helps.
yep, the problem is about context. "this" returns not what you expect it to return.
you can use
let self = this;
or you can use bind
function(){this.method}.bind(this);
the second method is better.
Also google something like "how to define context in js", "bind call apply js" - it will help you to understand what is going wrong.
// update component's data with some object's fields
// bad idea, use at your own risk
Object
.keys(patch)
.forEach(key => this.$data[key] = patch[key])

Angular - pass info from array from one controller to another

I'm having a little problem trying to pass a service within controllers.
What I'm trying to do is a shopping cart, I have a list of items and when I hit a button, those items get added to the cart, then I want to list those items in the cart in a separate page using a separate controller, so I'm trying to use a factory for the cart, but I don't know if you can set a factory object within a controller.
Here's my code, hope you can point me in the right direction.
var app = angular.module("Shop", []);
app.factory('DataService', function () {
var cart = [];
var set = function (data) {
cart = data;
}
var get = function () {
return cart;
}
});
app.controller("catalogController", function ($scope, $http) {
$scope.bookStore = {
selected: {},
books: null
};
$scope.cart = [];
$http.get("json/books.json")
.success(function (data) {
console.log(data);
$scope.bookStore.books = data;
})
.error(function (err) {
});
$scope.addToCart = function (book) {
var found = false;
$scope.cart.forEach(function (item) {
if (item.id === book.id) {
item.quantity++;
found = true;
}
});
if (!found) {
$scope.cart.push(angular.extend({
quantity: 1
}, book));
}
};
$scope.removeFromCart = function (item) {
var index = $scope.cart.indexOf(item);
$scope.cart.splice(index, 1);
};
$scope.getCartPrice = function () {
var total = 0;
$scope.cart.forEach(function (product) {
total += product.price * product.quantity;
});
return total;
};
});
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService;
});
Change things a bit to something like:
app.factory('DataService', function () {
var cart = [];
return {
set: function (data) {
cart = data;
},
get: function () {
return cart;
},
add: function (item) {
cart.push(item);
}
}
});
...
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService.get();
});
And then move the $http.get method and all the operations on the card in the other controller to functions in the factory and declare them on the same way as the above Dataservice.get()
You should do something like this:
A service is a singleton in angular js, that's mean you only have one instance of this class in your app.
var app = angular.module("Shop", []);
app.factory('DataService', function ($http) { // usualy your service is the one which call your API (not your controller)
var cart = null; // the cart array is set in the instance of the class as private
return{ // here you declare all the functions you want to call from outside (your controllers)
set : function (data) {
cart = data;
},
get: function(){
return cart;
},
getFromAPI = function () { // the code you have in your controller should goes here
return $http.get("json/books.json")
.success(function (data) {
console.log(data);
cart = data; //now you set you cart variable
})
.error(function (err) {
});
},
});
Then in your controllers:
app.controller("catalogController", function ($scope, DataService) { // include your service as a dependency
$scope.bookStore = {
selected: {},
books: null
};
$scope.cartInCatalogController = DataService.get(); // it will set the value of cart that's in your service to your controller's scope
if(!$scope.cartInCatalogController) {// if it's null so call the API
DataService.getFromAPI()// this function should return a promise
.success(function(data){// so call the success function
$scope.cartInCatalogController = data;
})
.error(function(error){
// do something here if you want
});
});
You can do the same in your other controller.
About the addToCard function and other stuff I let you find it by yourself.
You can start from here :)

Update an item in the list

I'm a beginner on knockout.
I made this page:
http://jsfiddle.net/LhTx4/
I would like to update only the item that comes back from the sellIt function.
How can I do that?
You are setting the quantity property incorrectly. quantity is a ko observable so you need to use the syntax:
self.sellIt = function (product) {
$.post('/Product/SellIt', { id: product.id },
function (data) {
var res = Enumerable.From(self.products)
.Where("i => i.id == " + data.Id)
.Select("s => s");
res.quantity(data.Quantity); // this is the important bit!!
});
};
However, I think you could probably shorten your code down to just:
self.sellIt = function (product) {
$.post('/Product/SellIt', { id: product.id },
function (data) {
product.quantity(data.Quantity);
});
};

Categories