I am trying to get songs from soundcloud, I am using some input to set value and send it to my factory to get all the related list of songs and display it.
The issue is the the first time all works correctly, but when I am trying to input new values I am getting same results as first time.
My code looks like:
.controller('DashCtrl', function ($scope, SongsService) {
$scope.formData = {};
$scope.searchSong = function () {
SongsService.setData($scope.formData.songName);
};
UPDATE
the factory :
.factory('SongsService', function ($rootScope) {
var List = {};
List.setData = function (tracks) {
var page_size = 6;
SC.get('/tracks', {limit: page_size, linked_partitioning: 1}, function (tracks) {
// page through results, 100 at a time
List = tracks;
$rootScope.$broadcast('event:ItemsReceived');
});
};
List.getItems = function () {
return List;
};
return List;
}).value('version', '0.1');
Thanks for help!
It's hard to tell without a plunkr reproducing the issue and showing all your relevant code, but I think your problem is that you're overwriting the List variable in the async answer, and this List (I assume) is the object you originally returned from your factory.
I see two noteworthy concepts here:
the fact that angular factories are effectively singletons
and that javascript objects are passed by reference-by-value (see call-by-sharing, or one of many stackoverflow discussions).
An angular factory is a singleton, meaning the factory function will only be called once, before the first injection, and every controller it's injected into will work with the same object reference it returned. If you overwrite this object reference, well, the previous value (which the controller has) is still a reference to the original object.
Edit: In fact, by overwriting List you're creating a new object which doesn't even have a setData method anymore!
You probably want to make List private to SongsService, and return a more complex object from the factory that captures List in a closure, and offers some public getter/setter methods for it. (If you insist on replacing the contents of the returned List variable, empty the object and extend it with the new properties - including this method again. But this is much more work, and not a nice solution.)
In Angular Service constructors and Factory methods are singleton objects. You need to return a method that you can call. Your code examples are incomplete so it is hard to tell what is going on. What is returned by your factory method, the List object?
If so, when the first call is completed, it overwrites the List object so that the setData method can't be called a second time. What is the SC object, I can not see in your example how you are injecting it. You probably want to fix that too.
Consider this possible solution.
Service
Songs.$inject = ['$http'];
function Songs($http) {
this.$http = $http;
}
Songs.prototype.getSongs = function(searchTerm) {
return this.$http.get('http://someendpoint/songs', {searchTerm: searchTerm});
}
service('songs', Songs);
Controller
DashController.$inect = ['songs'];
functionDashController(songs) {
this.songs = songs;
this.results = [];
}
DashController.prototype.searchSongs = function(searchTerm) {
var self = this;
this.songs.getSongs(searchTerm).then(function(results) {
this.results = results;
});
}
controller('DashController', DashController);
This is example uses the best practice controllerAs syntax explained here: http://toddmotto.com/digging-into-angulars-controller-as-syntax/
I found the issue,
I got same results all the time because I didnt use cooreclty the api of soundcloud, I didnt send the title on the api... also you are correct, I should not set the list as empty..I should set some value to the list...
Related
I am developing an AngularJS application and found the following behavior.
I have two functions in my service. The first function returns all the categories stored in the database and the second returns one category by its id.
Here is my service:
angular.module('categoriesRepository', [])
.service('categoriesRepository', ['$cordovaSQLite', 'sqliteHelper',
function ($cordovaSQLite, sqliteHelper) {
//this works - returns an array with all categories
this.getAll = function () {
var categories = [];
$cordovaSQLite.execute(sqliteHelper.getDb(),
"SELECT * FROM categories;")
.then(function (res) {
for (var i = 0; i < res.rows.length; i++) {
categories.push(res.rows[i]);
}
});
return categories;
}
//this works not - returns undefined
this.getById = function (id) {
var category;
$cordovaSQLite.execute(sqliteHelper.getDb(),
"SELECT * FROM categories WHERE id = ?;", [id])
.then(function (res) {
category = res.rows[0];
});
return category;
}
}]);
I know that I can use Angulars $q to run functions asynchronously, and use their values when they are done processing.
Why does the getById function return the category directly and the getAll wait until the array is filled?
EDIT
I had the getAll function posted wrong. There is no return statement before $cordovaSQLite.execute
UPDATE:-
After your question is updated.
In the first example your are creating an array first by doing var categories = [];and then returning this array before finishing your async call. When your async call completes it just pushes certain elements into the array thus not destroying the reference to the array (categories ) variable. When it is returned back if you will debug it you will find the function returning an empty array and later when the async call succeeds only then the array will be filled.
In the second example you are creating just a variable and then returning it before the async call finishes. But then the async call is finished you assign the variable to a new value. thus destroying the earlier reference.
Solution:-
Though not a preffered approach to make it work. you will have to maintain the category variable reference. for this you can use angular.copy OR angular extend
So the second part of your code should be like
this.getById = function (id) {
var category;
$cordovaSQLite.execute(sqliteHelper.getDb(),
"SELECT * FROM categories WHERE id = ?;", [id])
.then(function (res) {
angular.copy(res.rows[0], category);
//now the reference to the category variable
//will not be lost
});
return category;
}
Better Practice:-
The way you have been developing this application is wrong. Async calls should not be handled this way. I earlier asked a question just to clarify the way to handle the async calls and state inside the angular app, factories and controllers please have a look here. It provides two ways to handle the state and async calls. There might be many more practices out there but these two suit me best.
It is unfortunate that this approach appears to 'work' because it is caused by the modification of the returned array object "at some unspecified time" after it is returned.
In the usage the array is accessed/observed after1 it has been modified by the asynchronous call. This makes it appear to function correctly only because of the (accidental) asynchronous-later-than observation.
If the observation was prior to the actual completion of the SQLite operation - such as immediately after the getAll function call - it would reveal an empty array.
Both functions are incorrectly written and the first accidently releases Zalgo (or perhaps his sibling).
See How do I return the response from an asynchronous call? for more details.
1 Chrome's console.log can be confusing as it works like console.dir and thus may be showing the current value and not the value when it was invoked.
As stated already, this is a bad approach. You can't use result of your function immediately after it returns.
However I didn't see the answer to your exact question: why do they behave differently?
It happens because with an array you return a reference to an object (type Array). Later on you use same reference to modify contents of the object, i.e. push new items into the array.
However in second function you modify the reference itself. You make you local variable categories point to a new object. Thus old object (reference to which was returned to outer scope) remains untouched. To make it work the same way you should have written
category.row = res.rows[0];
You return the result of the execute in the first case, whereas you return the variable in the second case, which has most likely not been populated yet.
I am using http get function to get json array from service. How can I set response to controller locations element.
function MainCtrl($http,myService) {
this.locations = null;
myService.async().then(function(d) {
? = d;
});
}
Response d is well-formed (JSON)
You just take a reference to the current context and then use that variable reference in the callback
function MainCtrl($http,myService) {
var vm = this;
vm.locations = []; // initialise properly
myService.async().then(function(d) {
vm.locations = d;
});
}
There is a different approach to this problem too. I added my 'answer' for the sake of completeness. It is also possible to bind the this reference to your function. This can be achieved in two ways, by either using the angular.bind() or the native javascript bind to pass this reference.
e.g.:
function MainCtrl($http,myService) {
this.locations = []; // initialise properly
myService.async().then(function(d) {
this.locations = d;
}.bind(this));
}
P.S.: I tried to edited your answer and add this solutions to this problem of different scopes but it got rejected. For the following reason:
This edit was intended to address the author of the post and makes no sense as an edit. It should have been written as a comment or an answer
Not to take aim at the reviewers but they sure don't realize I can't post any comment with the current rep I have nor does it make sense to answer an answered question.
I have an array of "users" in a KnockoutJS observable array. I can add users to the array and it updates the view correctly, but the editing of users is currently driving me nuts, probably because of a slight lack of understanding of the base concept on my part.
View Model
var viewModel = {
users: ko.observableArray(),
user: ko.observable(),
showEditForm: function (model) {
if (!$('#users-form').is(':visible')) {
$('#mask').show();
}
showUsersLoading();
loadUserIntoEditForm(model.Id);
},
getUser: function (userId) {
for(var i = 0; i < this.users().length; ++i)
{
if (this.users()[i].Id === userId)
{
this.user(this.users()[i]);
break;
}
}
}
};
User View Model (this is primarily used for the add functionality at the moment)
var userViewModel = function (id, username, statusDescription, email) {
var self = this;
self.Id = ko.observable(id),
self.Name = ko.observable(username),
self.StatusDescription = ko.observable(statusDescription),
self.Email = ko.observable(email)
};
The updating / editing is performed in an MVC partial view that fires off an ajax request to update the user server side, then on a successful response, runs the following code to update the user
viewModel.getUser(result.Id);
viewModel.user().StatusDescription('locked');
viewModel.user().Name('testingUpdate');
Which gives me a Uncaught TypeError: string is not a function error
This is where my understanding fails me. I get that the user that I've grabbed from the users array doesn't have observable properties, which is why I can't update using the Knockout function method, I've confirmed this by pulling out details of the users array in the browser console window.
I also know that, conceptually, I want to cast the user observable object to a userViewModel object so the properties then become observable and I can update them; or I want the users observable array to know that the objects it contains should be of type userViewModel, so the object properties are observable.
The problem I'm having is although I understand the concept, I can't figure out the code to actually make it work.
The problem is the this keyword. In your sample it's not referring to what you expect.
Try using the RMP (revealing module pattern) to simplify the way you write your code. It looks something like this:
var viewModel = (function() {
var users = ko.observableArray();
var user = ko.observable();
// This one changes: you can use the vars directly
var getUser = function (userId) {
for(var i = 0; i < users().length; ++i)
{
if (users()[i].Id === userId)
{
user(this.users());
break;
}
}
}
// Reveal the data
return {
users: users,
user: user,
getUser : getUser
};
})(); // self-executing anonymous function
The anonymous function puts a closure around your vars. You can use the vars safely inside that closure, and they are not available outside of it. Besides you can have "private vars" by simply don't revealing them.
Additional note: I recommend you to use lodash or underscore to simplify array manipulation: you'd avoid writing loops to find an element in an array. For example using lodash find.
As always, the easy answer is, I'm an idiot.
The key part that I was missing is forgetting that I'm not dealing with strongly typed objects, and assuming that they'll just work the way I expect.
The success callback for my ajax call to populate the users() array in the viewModel object used to be
success: function (data) {
viewModel.users(data)
setupDatatable();
}
Which, looking back at it, the problem is obvious. I'm inserting generic objects into the array, not userViewModel objects.
So, when I changed it to:
success: function (data) {
for (var i = 0; i < data.length; ++i) {
viewModel.users.push(new userViewModel(data[i].Id, data[i].Name, data[i].StatusDescription, data[i].Email));
}
setupDatatable();
}
Suddenly it started working.
Hopefully, my stupidity and days worth of headaches will help someone else :)
So I have two viewModels, one has a document style database in an observable:
var databaseViewModel = function () {
var self = this;
self.database = ko.observableArray([]).publishesTo("database");
}
var calcViewModel = function () {
var self = this;
self.replicatedDatabase = ko.observableArray([]).subscribeTo("database");
}
These get applied thusly:
ko.applyBindings({
databaseViewModel: new databaseViewModel(),
calcViewModel: new calcViewModel()
});
The only problem is, that the drop down box tied to replicatedDatabase doesn't show any items. I know I can force a binding update:
database.valueHasMutated();
But I don't know where and when.
I have tried after the ko.applyBindings however, I'm not sure how to access the newly created databaseViewModel. I've tried inside databaseViewModel after it has been created, but I believe that KO automatically updates the bindings when they've been binded, so not sure this actually makes a difference, it didnt on the dropdowns anyways.
I'm not really sure what I should be doing here.
For reference, I'm using knockout-postbox to do message bus style communications, subscribeTo and publishesTo. So as soon as the database observable is changed it will notify all subscribers, so I thought that maybe replicatedDatabase would have been update in the instance that databaseViewModel was initiated.
So, rather than force knockout to update the values I chose a different approach.
Realistically speaking the page would initially be populated with some data from a server, so with this in mind I proceeded by making a global variable holding the initial data:
var serverData = [{}];
Then just simply populate the observableArray's using Ryan Niemeyer mapping function:
ko.observableArray.fn.map = function ( data, Constructor) {
var mapped = ko.utils.arrayMap(data, function (item) {
return new Constructor(item);
});
this(mapped);
return this;
};
This way both viewModel's start off with the initial data, and when the database viewModel gets updated this permeates through to the other viewModel's
EDIT:
Everything is working as I expected. It was just an error calling the template method. I mistyped a () so I was trying template.method instead of template().method;
Anyway, if somebody would like to explain me if this is a valid design pattern or if I should go in a different way I will be definitively very grateful.
I read about the module pattern and I'm trying to implement it in some of my projects. The problem is that, in my opinion, I'm twisting it too much.
I'm inspired by the google apps script style where many objects returns other objects with methods and so on and they pass arguments.
something like
object.method(var).otherMethod();
What I want to achieve is a method that receives a parameter, sets an internal variable to that parameter and then returns an object with methods that uses that variable. Here is a minified version of the code that does not work:
var H_UI =(function (window) {
var selectedTemplate,
compileTemplate = function(){},
parseTemplateFields = function(){};
//template subModule. Collect: collects the template fields and returns a JSON representation.
var template = function(templateString){
if(templateString) selectedTemplate = templateString;
return {
getHtml:function(){ return compileTemplate( parseTemplateFields( selectedTemplate ) ) } ,
collect:function(){
.. operating over selectedTemplate ...
return JSON.stringify(result)}
} };
return {
template:template
};
})(window);
If I remove the line :
if(templateString) selectedTemplate = templateString;
and replace selectedTemplate with the parameter templateString in the methods of the returned object it works as expected. I know that I cant create a set() method in the returned object and use it like this
H_UI.template().set(var)
But I find it ugly. Anyway I think that I'm messing things up.
What is the best way to construct this?
If you want H_UI.template() creates a new object every time you call template() on it, your solution does not work. Because the variable selectedTemplate is created only once when the immediate function is called.
However if your intent is this your solution works fine. (variable selectedTemplate is shared for all calls to template()).
But if you want to every call to template creates a new object. Please tell me to write my idea
Is this a valid design pattern or if I should go in a different way
Yes, enabling chaining is definitely a valid design pattern.
However, if your template() method returns a new object, that object and its methods should only depend on itself (including the local variables and parameters of the template call), but not on anything else like the parent object that template was called on.
So either remove that "global" selectedTemplate thing:
var H_UI = (function () {
function compileTemplate(){}
function parseTemplateFields(){}
// make a template
function template(templateString) {
return {
getHtml: function(){
return compileTemplate(parseTemplateFields(templateString));
},
collect: function(){
// .. operating over templateString ...
return JSON.stringify(result)
}
}
}
return {template:template};
})();
or make only one module with with a global selectedTemplate, a setter for it, and global methods:
var H_UI = (function () {
var selectedTemplate;
function compileTemplate(){}
function parseTemplateFields(){}
return {
template: function(templateString){
if (templateString)
selectedTemplate = templateString;
return this; // for chaining
},
getHtml: function(){
return compileTemplate(parseTemplateFields(selectedTemplate));
},
collect: function(){
// .. operating over selectedTemplate ...
return JSON.stringify(result)}
}
};
})();
The difference is striking when we make two templates with that method:
var templ1 = H_UI.template("a"),
templ2 = H_UI.template("b");
What would you expect them to do? In a functional design, templ1 must not use "b". With the first snippet we have this, and templ1 != templ2. However, if .template() is a mere setter, and every call affects the whole instance (like in the second snippet), we have templ1 == H_UI and templ2 == H_UI.