I can't understand what $scope.search is doing there, like, what it is doing when it set equals to the function? And, if i want to do in other way, how should i do?
(i am using angular js, 1.6 version)
$scope.search = function(){
query.get($scope.username , {
success: function(gameScore) {
console.log(gameScore);
return gameScore;
},
error: function(object, error) {
console.log("Sorry, this user does not exist yet");
}
});
};
You can call $scope.search() from anywhere in your controller. You can also do something like ng-click=search() on any element in a template that is using that controller.
Calling $scope.search() will either return gameScore or print an error message to the console.
Not sure if this function is written in a general controller or a component controller
Since you are using 1.6
Incase of it being a component controller you could attach this function to component controller and use it in your template or component
angular.module('app',[])
.component('testComponent', {
bindings: {}.
tempalate:'<div>$ctrl.getValue()</div>',
controller: function(){
var ctrl = this;
ctrl.getValue = function() {
console.log('log your value');
}
}
});
Related
I have a function inside a controller and I'm confused as to how to update a $scope variable from inside a .then function.
Heres my function:
searchSpotify(query, $scope) {
this.Spotify.search(query,'track').then(function (data) {
console.log(data.tracks.items); // this is working
$scope.result = data;
});
}
In the console log I receive this error:
TypeError: Cannot set property 'result' of undefined
Do I use $scope.$apply somehow?
EDIT:
Here's the entire controller for context
(function() {
class SelectionController {
constructor(User, groupService, selectionService, songService, $scope, $routeParams, Spotify) {
this.groupId = $routeParams.groupId;
this.selectionId = $routeParams.selectionId;
this.groupService = groupService;
this.selectionService = selectionService
//this.selections = this.selectionService.getSelections();
this.songService = songService;
this.searchResults;
this.$scope = $scope;
this.Spotify = Spotify
}
searchSpotify(query) {
this.Spotify.search(query, 'track').then(function(data) {
console.log(data.tracks.items);
$scope.result = data;
});
}
addSong(name, artist, album) {
alert('called from selection controller');
this.songService.addSong(this.selectionId, name, artist, album);
}
createSelection(name, description) {
this.selectionService.createSelection(this.groupId, name, description);
}
deleteSelection(selection) {
this.selectionService.deleteSelection(selection);
}
}
angular.module('songSelectionApp')
.controller('SelectionController', SelectionController);
})();
Save reference to $scope:
searchSpotify(query) {
var $scope = this.$scope;
this.Spotify.search(query, 'track').then(function(data) {
console.log(data.tracks.items);
$scope.result = data;
});
}
Or use arrow functions:
searchSpotify(query) {
this.Spotify.search(query, 'track').then((data) => {
console.log(data.tracks.items);
this.$scope.result = data;
});
}
.bind(this) should also work, as another answers suggest.
Regarding $scope.$apply: you need it only when you want to change a $scope outside of a $digest, for example when you use external (non-angular) libraries/functions, like WebSocket, or jquery event listeners. Untill Spotify is an angular service/factory - you don't need $scope.$apply
From docs:
$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
this.Spotify.search(query,'track').then(function (data) {
console.log(data.tracks.items); // this is working
this.result = data;
}.bind($scope));
Should be all you need really. You are basically telling the internal scope what this should really be
You should use
this.Spotify.search(query,'track').then(function (data) {
console.log(data.tracks.items); // this is working
this.$scope.result = data;
}.bind(this));
Whenever, I am testing a controller and have something like this in it.
$scope.isSomething = function (Item) {
return ItemCollection.someItem(Item.attachedItem);
};
giving error on karma console:
TypeError: undefined is not an object (evaluating 'Item.attachedItem')
I am simply calling the function from the test file like this:
scope.isSomething();
I need to mock the Item.attachedItem or I am missing something here.. Please Explain in details as this is happening in multiple files.. thanks in advance
Also, for this type of code
.controller('itemCtrl', function (itemCollection) {
var vm = this;
this.itemCollection= itemCollection;
itemCollection.someItem().then(function (Item) {
vm.pageUrl = Item.pageUrl;
vm.Item= Item.someItems;
});
});
Also, this is also part of the code for more broad view here it gives Item.pageUrl is not a object error
Refer angular unit testing docs
The ItemCollection being a service, you could inject a mock while initialising a controller using
var ItemCollection, ItemCrtl;
beforeEach(inject(function($controller, $rootScope) {
$scope = $rootScope.$new();
ItemCollection = jasmine.createSpyObj('ItemCollection', ['someItem']);
ItemCrtl = $controller('ItemCtrl', {
$scope: scope,
ItemCollection: ItemCollection
});
});
For Item, the method isSomething should take care of checking if Item is undefined before doing Item.attachedItem
Testing an aync block is tricky. someItem returns a promise. $q an angular service to which can be used create async functions while testing.
We need to resolve the deferred object to test the async task.
var ItemCollection, ItemCrtl, deferedObj;
beforeEach(inject(function($controller, $rootScope, $q) {
$scope = $rootScope.$new();
deferedObj = $q.defer();
ItemCollection = jasmine.createSpyObj('ItemCollection', ['someItem']);
ItemCollection.someItem.andReturn(deferedObj.promise);
ItemCtrl = $controller('ItemCtrl', {
$scope: scope,
ItemCollection: ItemCollection
});
});
it('sets page url', function() {
deferedObj.resolve({ pageUrl: 'http://url', someItems: [1,2,3] });
scope.$apply();
expect(ItemCtrl.pageUrl).toEqual('http://url');
});
you have to use mock Item data in test like this (assuming attachedItem value is boolean)
var item={attachedItem:true}
scope.isSomething(item)
$scope.isSomething = function (Item) {
if(!Item.attachedItem){
Item.attachedItem=YOUR_MOCK_VALUE;
}
return ItemCollection.someItem(Item.attachedItem);
};
I am working on a section of an AngularJS (1.4.2) app and trying to get my head around how to get a controller updated data from a service when its data changes.
I have set up this plunker to demonstrate the problem. I hope the example doesn't seem too convoluted, but in the app I have a main tables view like the one in the plunker, but with five tabs, each with its own partial html file and controller. Right now there's one service that holds all the fields data, much like in the plunker. The idea is that users can check the box next to a given field to activate/deactivate it as it suits their data, and only active fields should be persisted in the database, thus the service returning filtered data to the controller. The verifyFields() function in MainCtrl simulates the save function in my app, and the controller should be populating the newConnectionData object with updated data from the fields service.
I've included a pre to show that the service model data does indeed update when the checkbox is checked and unchecked or when the input value changes. But I've so far been unable to get the data to update in the main controller.
I tried to incorporate separately in the plunker solutions from this stack overflow question that suggests returning a function rather than a primitive from the service:
test.service('fields', function() {
...
var fields = this;
return {
...
activeOrders : function() { return filteredOrders },
activeShipment : function() { return filteredShipment },
activeActivity : function() { return filteredActivity }
}
test.controller('MainCtrl', ['$scope', '$rootScope', 'fields',
function($scope, $rootScope, fields) {
...
$scope.activeShipment = function () {
return fields.activeShipment();
};
$scope.activeActivity = function () {
return fields.activeActivity();
};
...and this one that suggests using a $watch:
$scope.$watch(function () { return fields.activeOrders() }, function (newVal, oldVal) {
if (typeof newVal !== 'undefined') {
$scope.activeOrders = fields.activeOrders();
}
});
$scope.newConnectionData = {};
...
$scope.verifyFields = function () {
$scope.newConnectionData = {
orders : $scope.activeOrders,
shipment : $scope.activeShipment(),
activity : $scope.activeActivity()
}
...
}
...but neither has thus far solved the issue of updating the data in the controller. If you have any suggestions, I would be much obliged. Thank you for your time!
let the service expose data as properties. Don't over complicate usage in controller. Just use directly. If you need to show data in html, add reference to the service in $scope.
test.service('fields', function() {
...
var fields = this;
return {
...
activeOrders : filteredOrders ,
activeShipment : filteredShipment ,
activeActivity : filteredActivity
In controller
test.controller('MainCtrl', ['$scope', '$rootScope', 'fields',
function($scope, $rootScope, fields) {
$scope.fields = fields;
In template
{{fields.activeShipment}}
It looks like you may need to recalculate your _.where with the function calls.
Your factory service's would have lines like:
activeOrders : function() { return _.where(fields.ordersSchema, {active: true} ); },
Here is what I ended up with.
I want to capture the url of my query in an AngularJS service as this
var mortgageloanService = angular.module('loanstreetIpadAppApp', []);
mortgageloanService.factory('updateTable', function($http) {
return {
getParams: function() {
var url = 'https://api.mongolab.com/api/1/databases/angularjs-intro/collections/users?apiKey=terrPcifZzn01_ImGsFOIZ96SwvSXgN9';
console.log('in service mode');
console.log(url);
return $http.get(url);
}
};
});
This is my controller.js code
angular.module('loanstreetIpadAppApp')
.controller('Mortgage_LoanCtrl', function ($location, $scope) {
$scope.update_result = function(updateTable) {
updateTable.getParams().success(function(loan){$scope.loan = loan});
console.log($scope.resulttable);
};
});
On my view page, i have a button which onclick shud call the update_result function. But whenever i click on the button i get the following error
TypeError: Cannot read property 'getParams' of undefined
at Scope.$scope.update_result (http://localhost:9003/scripts/controllers/mortgage_loan.js:22:16)
at http://localhost:9003/bower_components/angular/angular.js:10567:21
at http://localhost:9003/bower_components/angular/angular.js:18627:17
at Scope.$eval (http://localhost:9003/bower_components/angular/angular.js:12412:28)
at Scope.$apply (http://localhost:9003/bower_components/angular/angular.js:12510:23)
at HTMLButtonElement.<anonymous> (http://localhost:9003/bower_components/angular/angular.js:18626:21)
at HTMLButtonElement.jQuery.event.dispatch (http://localhost:9003/bower_components/jquery/dist/jquery.js:4430:9)
at HTMLButtonElement.elemData.handle (http://localhost:9003/bower_components/jquery/dist/jquery.js:4116:28)
Anyone knows how to solve this issue?
In order to use your updateTable's factory inside your controller, you need to inject it. So, your controller should look like this.
angular.module('loanstreetIpadAppApp')
.controller('Mortgage_LoanCtrl', function ($location, $scope, updateTable) {
$scope.update_result = function() {
updateTable.getParams().success(function(loan){$scope.loan = loan});
console.log($scope.resulttable);
};
});
Notice that I've removed "updateTable" as "$scope.update_result"'s parameter since it would overwrite your updateTable object inside that closure.
I have a provider:
AdviceList.provider('$adviceList',function(){
this.$get = function ($rootScope,$document,$compile,$http,$purr){
function AdviceList(){
$http.post('../sys/core/fetchTreatments.php').success(function(data,status){
this.treatments = data;
console.log(this.treatments); // the correct object
});
this.adviceCategories = [
// available in the controller
];
}
return{
AdviceList: function(){
return new AdviceList();
}
}
}
});
Further, i have this controller:
AdviceList.controller('AdviceListCtrl',function($scope,$adviceList){
var adv = $adviceList.AdviceList();
$scope.treatments = adv.treatments; // undefined
});
Why is it, that the controller's $scope.treatments stays undefined, this.treatments inside the provider however, is filled correctly? Also, adviceCategories is available in my controller.
The call you get teatment is async in nature so the results may not have been populated when you try to assign them.
So here
var adv = $adviceList.AdviceList();
$scope.treatments = adv.treatments; //The treatments would only get filled after the server call is over.
You need to rewrite the code in a way that you assign it to your scope property on the success callback.
I will recommend you to simplify your code
1) Use simple factory method of angular instead of provider
2) return a promise to avoid using callbacks
AdviceList.service('adviceList', function ($http) {
return {
adviceList: function () {
return $http.post('../sys/core/fetchTreatments.php');
}
}
});
AdviceList.controller('AdviceListCtrl', function ($scope, $adviceList) {
adviceList.AdviceList().then(function (data) {
$scope.treatments = data //set value to data when data is recieved from server
});
});