I have a bit of Angular code that I cannot seem to debug. The page on load renders all of my groups perfectly in the navigation drop down bar. When I go to click on one of the group, the messagesIndex function is called and all of the messages from that group will be displayed normally and at this point everything is working fine. The problem is when I go to click on another group, or even the same group, the messagesIndex function does not load a second time.
I have spent a lot of time trying to debug this issue to no avail, but I have narrowed down exactly where the problem is occurring, there just doesn't seem to be any issues there that I can find.
I replaced the contents of the messagesIndex function with just $scope.groupID, and outputted the result along with a random number. the groupID displayed along with the random number, and every time I clicked different groups different IDs and random numbers appeared, which means that the JADE/HTML is functioning properly and the function headers are golden.
I then took the original messagesIndex function and added $scope.random1 and scope.random2 with random numbers assigned to them to both the beginning and the end of the function, both before and after the HTTP request. When I called the function the first time, both random numbers generated and were outputted, though the http request is asynchronous all values were returned normally, hinting that the function called completely, unless there was an issue with the HTTP request dragging on after the success response? Then when I went to call it again by clicking another group. neither of the random numbers changed, showing that the function either did not call at all, or it stopped before executing the first line of code within it.
frankly speaking I am stumped. The code looks basically Identical to many working HTTP request, yet for some reason I cannot call it twice.
Below is the AngularJS code:
angular.module('angular-client').controller('GroupMeCtrl',
['$scope', '$http', '$templateCache', 'Auth', function($scope, $http, $templateCache) {
$scope.groupsIndex = function(){
$http({
method: 'GET',
url: 'https://api.groupme.com/v3/groups?token=' + token,
cache: $templateCache
}).
success(function(data, status) {
$scope.groupsIndex = data.response;
$scope.stat = status;
}).
error(function(data, status, headers, config) {
$scope.groupsIndex = data.response;
$scope.stat = status || "Request failed";
});
};
$scope.messagesIndex = function($groupID){
$http({
method: 'GET',
url: 'https://api.groupme.com/v3/groups/' + $groupID + '/messages?token=' + token,
cache: $templateCache
}).
success(function(data, status) {
$scope.messagesIndex = data;
$scope.stat = status;
}).
error(function(data, status) {
$scope.messagesIndex = data.response;
$scope.stat = status || "Request failed";
});
};
}]);
And below here is my JADE:
li.dropdown
a.dropdown-toggle(href='#', data-toggle='dropdown')
| Groups
b.caret
ul.dropdown-menu
li(ng-repeat="group in groupsIndex")
a(ng-click="messagesIndex(group.id)") {{group.name}}
.message-box(ng-repeat="message in messagesIndex.response.messages")
.avatar-container
img(src="{{message.avatar_url}}").avatar
.text
.user-name
{{message.name}}
span
{{message.text}}
You're overwriting the function with the data returned:
$scope.messagesIndex = function($groupID){
...
$scope.messagesIndex = data;
...
$scope.messagesIndex = data.response;
$scope.messagesIndex can't equal a function and the data you receive.
Related
I have been testing this code for 2 months, it is the first exercise in my tutorial to learn AngularJS.
The challenge is to count all .json files in a folder and increment it with 1 so that when I save another json file it will always have a higher ID then the previous one. I am having lots of trouble with web servers, first of all NodeJS does not seem to allow JSON posts in its standard configuration. So I have found a modified web-server.js from stockoverflow from a different question:
$resource.save is not functioning
https://github.com/glepretre/angular-seed/commit/9108d8e4bf6f70a5145b836ebeae0db3f29593d7#diff-d169b27b604606d4223bd5d85cad7da1 I have also tried the web-server.js that came with the tutorial:
http://pastebin.com/Ckfh4jvD that seemed to work better. WAMP also did not work I could not get Apache to allow JSON posts.
Problem is the web-server posts the json or sees the json as an object not as an array, even though I have used "isArray: true" and I use .query() instead of .get(). And I have tried many other things like transformResponse: []. I need the array to get .length to work! Also sometimes it GETS an array and POSTS an object which it later reads as object again it is getting really weird.
The code works sometimes as posted or sometimes I need to change :id to :id.json, usually this means the server is retrieving it as an object again which is not what I wan but this differs between the 2 nodeJS servers.
.factory('eventData', ['$resource', '$q', function ($resource, $q) {
var resource = $resource('/app/data/event/:id', {id: '#id'}, {"getAll": {method: "GET", isArray: true}});
var number = resource.query();
console.log(number);
return {
getEvent: function () {
var deferred = $q.defer();
resource.get({id: 1},
function (event) {
deferred.resolve(event);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
},
save: function (event) {
var deferred = $q.defer();
event.id = number.length;
resource.save(event,
function (response) {
deferred.resolve(response);
},
function (response) {
deferred.reject(response);
}
);
return deferred.promise;
}
};
}]);
EDIT: This seems to work better however I need to figure out how to put an .then() into this service?
.factory('eventData', ['$resource', '$q', function ($resource, $q) {
var resource = $resource('/app/data/event/:id.json',
{id: '#id'}, {method: "getTask", q: '*' },
{'query': { method: 'get'}});
var number = resource.query();
I use a form to submit values. When I click on the submit button, it calls edit().
Values are stored in the database (with php/mysql). Everything works fine so far.
I retrieve the values on the same partial view. On window.location.reload(); the page will refresh but the values are not instantly updated. After several refresh, it's
(When my DevTools is open, the cache is disable... so it's instantly updated)
Without the DevTools open, the cache works even if I put some function to disable the cache.
listingProjectApp.run(function($rootScope, $templateCache) {
$rootScope.$on('$viewContentLoaded', function() {
$templateCache.removeAll();
});
});
listingProjectApp.factory('EditCache', function($cacheFactory) {
return $cacheFactory('editCache', {
capacity: 1
});
});
listingControllers.controller('editProjectCtrl', ['$scope', '$http', '$routeParams', 'EditCache',
function ($scope, $http, $routeParams, EditCache) {
var result = EditCache.get('$http');
if( result ){
$scope.project = result;
console.log("Results from cache");
}
else {
$http({
url: "php/getProjectDetails.php",
method: "GET",
cache: false,
params: { 'id': $scope.projectId }
}).success(function(data) {
$scope.project = data;
EditCache.put('$http', data);
console.log("New results");
});
}
console.log(EditCache.info())
$scope.edit = function(project){
console.log(project);
$http({
url: "php/edit.php",
method: "POST",
data: project
}).success(function(id) {
EditCache.removeAll();
window.location.reload();
});
return false;
}
}]);
Looks like it's not the AngularJS cache's fault, but rather the browser's: for the browser, your XHR requests are simple HTTP GET requests, and all caching policies are applied to them. To avoid caching the partials, you'll have to either tweak your back-end to send appropriate headers to forbid caching, or use the old random suffix trick: add another dummy parameter to your params (like dummy: 'some-random-string'). This way, every GET request will be unique, and you'll not hit the cache.
Iam using asp.net mvc4 and facing some problem in accessing viewbag.price.
This is what i am doing:-
[HttpPost]
public ActionResult FillModel(int id)
{
var vehModel = db.Vehicle_Model.Where(vehMod => vehMod.MakeID == id).ToList().Select(vehMod => new SelectListItem() { Text = vehMod.Model, Value = vehMod.pkfModelID.ToString() });
ViewBag.Price = 100;
return Json(vehModel, JsonRequestBehavior.AllowGet);
}
i am calling above using below:-
$.ajax({
url: '#Url.Action("FillModel","Waranty")',
type: 'post',
data: { id: id },
dataType: 'json',
success: function (data) {
$('#ddModel').empty();
$.each(data, function (index, val) {
var optionTag = $('<option></option>');
$(optionTag).val(val.Value).text(val.Text);
$('#ddModel').append(optionTag);
});
var a = '#ViewBag.Price';
},
error: function () {
alert('Error');
}
});
But i am not able to access ViewBag.Price.
Anyone know the reason??
thanks
The reason you aren't able to access items from the ViewBag inside your ajax success function is because the view that contains your script has already been rendered by the Razor view engine, effectively setting the variable a to whatever the value of #ViewBag.Price was at the time the page was rendered.
Looking at the process flow might be helpful:
(1) The request comes in for the view that has your script fragment in it.
(2) The controller method that returns your view is called.
(3) The Razor view engine goes through the view and replaces any references to #ViewBag.Price in your view with the actual value of ViewBag.Price. Assuming ViewBag.Price doesn't have a value yet, the success function in your script is now
success: function (data) {
$('#ddModel').empty();
$.each(data, function (index, val) {
var optionTag = $('<option></option>');
$(optionTag).val(val.Value).text(val.Text);
$('#ddModel').append(optionTag);
});
var a = '';
}
(4) The rendered html gets sent to the client
(5) Your ajax request gets triggered
(6) On success, a gets set to the empty string.
As you had mentioned in the comments of your question, the solution to this problem is to include a in the Json object returned by your action method, and access it using data.a in your script. The return line would look like
return Json(new {
model = vehModel,
a = Price
});
Keep in mind that if you do this, you'll have to access model data in your ajax success function with data.model.Field. Also, you shouldn't need to specify the JsonRequestBehavior.AllowGet option, since your method only responds to posts and your ajax request is a post.
I have a service which retrieves data from the datastore (Web SQL). Afterwards, it stores the data in a AngularJS array. The problem is that this does not initiate changes to the UI.
Contrary, if after the retrieval of data from datastore, I call a web services using a $get method and append the results to the previous array, all data updates the UI.
Any suggestions? Is it possible that I fill the array before the Angular binds the variable?
Can I somehow delay the execution of the service?
Most of the code has been taken from the following example: http://vojtajina.github.io/WebApp-CodeLab/FinalProject/
In order for the UI to magically update, some changes must happen on properties of the $scope. For example, if retrieving some users from a rest resource, I might do something like this:
app.controller("UserCtrl", function($http) {
$http.get("users").success(function(data) {
$scope.users = data; // update $scope.users IN the callback
}
)
Though there is a better way to retrieve data before a template is loaded (via routes/ng-view):
app.config(function($routeProvider, userFactory) {
$routeProvider
.when("/users", {
templateUrl: "pages/user.html",
controller: "UserCtrl",
resolve: {
// users will be available on UserCtrl (inject it)
users: userFactory.getUsers() // returns a promise which must be resolved before $routeChangeSuccess
}
}
});
app.factory("userFactory", function($http, $q) {
var factory = {};
factory.getUsers = function() {
var delay = $q.defer(); // promise
$http.get("/users").success(function(data){
delay.resolve(data); // return an array of users as resolved object (parsed from JSON)
}).error(function() {
delay.reject("Unable to fetch users");
});
return delay.promise; // route will not succeed unless resolved
return factory;
});
app.controller("UserCtrl", function($http, users) { // resolved users injected
// nothing else needed, just use users it in your template - your good to go!
)
I have implemented both methods and the latter is far desirable for two reasons:
It doesn't load the page until the resource is resolved. This allows you to place a loading icon, etc, by attaching handlers on the $routeChangeStart and $routeChangeSuccess.
Furthermore, it plays better with 'enter' animations in that, all your items don't annoyingly play the enter animation every time the page is loaded (since $scope.users is pre populated as opposed to being updated in a callback once the page has loaded).
Assuming you're assigning the data to the array in the controller, set an $scope.$apply() after to have the UI update.
Ex:
$scope.portfolio = {};
$scope.getPortfolio = function() {
$.ajax({
url: 'http://website.com:1337/portfolio',
type:'GET',
success: function(data, textStatus, jqXHR) {
$scope.portfolio = data;
$scope.$apply();
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(errorThrown);
}
});
};
So, I'm using FineUploader 3.3 within a MVC 4 application, and this is a very cool plugin, well worth the nominal cost. Now, I just need to get it working correctly.
I'm pretty new to MVC and absolutely new to passing back JSON, so I need some help getting this to work. Here's what I'm using, all within doc.ready.
var manualuploader = $('#files-upload').fineUploader({
request:
{
endpoint: '#Url.Action("UploadFile", "Survey")',
customHeaders: { Accept: 'application/json' },
params: {
//variables are populated outside of this code snippet
surveyInstanceId: (function () { return instance; }),
surveyItemResultId: (function () { return surveyItemResultId; }),
itemId: (function () { return itemId; }),
imageLoopCounter: (function () { return counter++; })
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png', 'bmp']
},
multiple: true,
text: {
uploadButton: '<i class="icon-plus icon-white"></i>Drop or Select Files'
},
callbacks: {
onComplete: function(id, fileName, responseJSON) {
alert("Success: " + responseJSON.success);
if (responseJSON.success) {
$('#files-upload').append('<img src="img/success.jpg" alt="' + fileName + '">');
}
}
}
}
EDIT: I had been using Internet Explorer 9, then switched to Chrome, Firefox and I can upload just fine. What's required for IE9? Validation doesn't work, regardless of browser.
Endpoint fires, and file/parameters are populated, so this is all good! Validation doesn't stop a user from selecting something outside of this list, but I can work with this for the time being. I can successfully save and do what I need to do with my upload, minus getting the OnComplete to fire. Actually, in IE, I get an OPEN/SAVE dialog with what I have currently.
Question: Are the function parameters in onComplete (id, filename, responseJSON) getting populated by the return or on the way out? I'm just confused about this. Does my JSON have to have these parameters in it, and populated?
I don't do this (populate those parameters), and my output method in C# returns JsonResult looking like this, just returning 'success' (if appropriate):
return Json(new { success = true });
Do I need to add more? This line is after the saving takes place, and all I want to do is tell the user all is good or not. Does the success property in my JSON match up with the responseJSON.success?
What am I missing, or have wrong?
Addressing the items in your question:
Regarding restrictions inside of the "select files" dialog, you must also set the acceptFiles validation option. See the validation option section in the readme for more details.
Your validation option property in the wrong place. It should not be under the request property/option. The same is true for your text, multiple, and callbacks options/properties. Also, you are not setting your callbacks correctly for the jQuery plug-in.
The open/save dialog in IE is caused by your server not returning a response with the correct "Content-Type" header. Your response's Content-Type should be "text/plain". See the server-side readme for more details.
Anything your server returns in it's response will be parsed by Fine Uploader using JSON.parse when handling the response client-side. The result of invoking JSON.parse on your server's response will be passed as the responseJSON parameter to your onComplete callback handler. If you want to pass specific information from your server to your client-side code, such as some text you may want to display client-side, the new name of the uploaded file, etc, you can do so by adding appropriate properties to your server response. This data will then be made available to you in your onComplete handler. If you don't have any need for this, you can simply return the "success" response you are currently returning. The server-side readme, which I have linked to, provides more information about all of this.
To clarify what I have said in #2, your code should look like this:
$('#files-upload').fineUploader({
request: {
endpoint: '#Url.Action("UploadFile", "Survey")',
customHeaders: { Accept: 'application/json' },
params: {
//variables are populated outside of this code snippet
surveyInstanceId: (function () { return instance; }),
surveyItemResultId: (function () { return surveyItemResultId; }),
itemId: (function () { return itemId; }),
imageLoopCounter: (function () { return counter++; })
}
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'gif', 'png', 'bmp']
},
text: {
uploadButton: '<i class="icon-plus icon-white"></i>Drop or Select Files'
}
})
.on('complete', function(event, id, fileName, responseJSON) {
alert("Success: " + responseJSON.success);
if (responseJSON.success) {
$('#files-upload').append('<img src="img/success.jpg" alt="' + fileName + '">');
}
});