angular $resource removes a property when object changes - angularjs

I've created a Service that returns a $resource
module.registerFactory('Website', function ($resource, $cacheFactory) {
var cache = $cacheFactory('websites');
var pagedCache = $cacheFactory('websites_paged');
return $resource('/api/websites/:id', {id: '#id'}, {
query: {method: 'GET', isArray: false, cache: pagedCache},
get: {method: 'GET', cache: cache}
});
});
In an edit state I receive all details by calling
$scope.website = Website.get({'id': $stateParams.id});
The $scope.website promise contains my data as expected. (Below a shortened JSON result from server)
{"id":25,"name":"blabla","url":"http://here.com","description":"blabla",
"tags":[
{"id":6,"name":"..."},
{"id":7,"name":"..."}
{"id":10,"name":"..."}
],
"objectives":[
{"id":3206,"code":"WIS AD3.c","name":"[ommitted objective 3206]","parent_id":3203},
{"id":3209,"code":"WIS AD4.b","name":"[ommitted objective 3209]","parent_id":3207}
]}
My problem is with the objectives property.
In my EditCtrl I open a modal and send the objectives as selected items to the modal. That works perfect.
$scope.selectObjectives = function () {
var modalInstance = $modal.open({
templateUrl: 'app/modules/objectives/templates/select-objectives.html',
controller: 'SelectObjectivesModalCtrl',
size: 'lg',
resolve: {
selectedItems: function () {
return $scope.website.objectives;
}
}
});
modalInstance.result.then(function (selectedItems) {
$scope.website.objectives = selectedItems;
console.log($scope.website);
});
}
When closing the modal the newly selectedItems are injected back into $scope.website.objectives. (cfr. modalInstance.result.then() ... )
The console logs perfectly all properties - including the objectives.
Now comes the weird part:
As soon as I try to access $scope.website in another function (ie update)
The objectives property is removed from $scope.website.
This is my update method:
$scope.updateWebsite = function () {
console.log($scope.website);
$scope.website.$save(function () {
$cacheFactory.get('websites').remove('/api/websites/' + $scope.website.id);
$cacheFactory.get('websites_paged').removeAll();
$state.go('app.websites');
});
};
The console logs all properties in $scope.website - except for the objectives. This is completely removed.
I hope I made myself clear enough.
Thanks for taking some time to help me pointing to the right direction.

My bad.
My response didn't return the entire object as should be in a RESTful POST.

Related

My $scope value not updating

updated object is not getting without using location.reload
Hi i will get list of objects from API where i have to update one value,After updating that value in modal popup i am not able to get new updated value without using location.reload. Is there any other solution for this.i am using two different controllers.
Will get list of objects from this
$scope.groups=response.data
will ng-repeat this groups where i will have edit button for every line,Once i click edit button popup will open
$scope.editGroup = function(u) {
$scope.groupName=u.groupName;
$scope.groupId=u.groupId;
ModalService.showModal({
templateUrl: 'app/components/modal/editGroupDetails.html',
controller: "ModalController",
scope: $scope
}).then(function(modal) {
modal.element.modal();
modal.close.then(function(result) {
$scope.message = "You said " + result;
});
});
};
Next once i edit the GroupName i will click submit
$scope.updateGroupName=function(){
var data={
"groupId": $scope.groupId,
"groupName": $scope.groupName
}
url=globalUrlConfig.globalAdminApi+globalUrlConfig.updateGroup+$scope.schoolId;
$http({
method: 'PUT',
url: url,
data: data,
}).success(function(response) {
console.log(response)
if(response._statusCode==200){
alert(response.data);
location.reload();
}
else{
alert("not updated")
}
})
}
Without using location.reload i am not able display updated data
Use $rootscope like this
Add this $rootScope.$emit("refreshData", {});
instead of Location.reload();
And then add this in your main controller for load data without refresh
var RefreshValue = $rootScope.$on("refreshData", function () {
GetValueMethod(); // call your method (List of object) for get data without reload form
});
$scope.$on('$destroy', RefreshValue);
Don't forget to inject $rootScope.

Mongoose and Angular Populate Issue

I am new to mongoose and Angular and I am having an issue with mongoose's populate method. I have the following two mongoose schemas
var JobSchema = new mongoose.Schema({
jobName: String,
jobType: String,
status: String,
examples: [{type: mongoose.Schema.Types.ObjectId, ref: 'Example'}]
});
mongoose.model('Job', JobSchema);
and
var ExampleSchema = new mongoose.Schema({
content: String,
job: {type: mongoose.Schema.Types.ObjectId, ref: 'Job'}
});
mongoose.model('Example', ExampleSchema);
So basically the Job schema contains Example's. I also have the following Express route for getting the examples from a particular Job. I used this tutorial to figure out how to do this.
var Job = mongoose.model('Job');
var Example = mongoose.model('Example');
router.get('/jobs/:job', function (req, res) {
req.job.populate('examples', function (err, job) {
if (err) {return next(err);}
res.json(job);
});
});
Also, I am using the following to automatically retrieve the job from mongo and attach it to req.
router.param('job', function (req, res, next, id) {
var query = Job.findById(id);
query.exec(function (err, job) {
if (err) {
return next(err);
}
if (!job) {
return next(new Error('can\'t find job'));
}
req.job = job;
return next();
});
});
I also have the following Angular factory that uses this route
app.factory('jobs', ['$http', function ($http) {
var o = {
jobs: []
};
o.get = function (id) {
return $http.get('/jobs/' + id).then(function (res) {
return res.data;
});
};
return o;
}]);
I also created the following state which is supposed to immediately populate the examples for a given Job id using the above factory.
.state('jobs', {
url: '/jobs/{id}',
templateUrl: '/jobs.html',
controller: 'NerCtrl',
resolve: {
post: ['$stateParams', 'jobs', function ($stateParams, jobs) {
return jobs.get($stateParams.id);
}]
}
});
The problem comes when I actually try to show the examples using a controller.
app.controller('NerCtrl', [
'$scope',
'job',
function ($scope, job) {
$scope.examples = job.examples;
}]);
The view that tries to use $scope.examples just displays {{examples}} rather than the actual content of the scope variable. In fact, nothing in the controller seems to work with the `job` injection (not even simple 'alerts').
It looks the problem comes from the `job` injection in the controller. This is supposed to refer to the job that is retrieved in the resolve given the id but it doesn't look like this is working.
In addition, I have curled an example record's url (eg. curl http://localhost:3000/jobs/56920a1329cda48f16fc0815) and it does return the desired Job record, so it does look like the route part is working correctly. I suspect the problem is somewhere in the 'resolve' or the way in which I am injecting the result of the resolve into the controller.
Ok this was a silly mistake. The post inside the Job state should have been job. i.e.
.state('jobs', {
url: '/jobs/{id}',
templateUrl: '/jobs.html',
controller: 'NerCtrl',
resolve: {
job: ['$stateParams', 'jobs', function ($stateParams, jobs) {
return jobs.get($stateParams.id);
}]
}
});
In my inexperience, I did not know what post was referring to, but I suppose it refers to the job that is returned from jobs.get($stateParams.id) which is then the name that gets injected in the controller. So obviously the name in resolve must be consistent with what is injected in the controller.

$http.get method not working on ng-submit

I want $http.get method to work when a form is submitted.
Here is my code. The object $scope.questions is being set when the method is called but the data doesn't show up in the div. Moreover, when the $http.get method is outside the signIn() function it works just fine.
$scope.signIn = function(data) {
$location.path('/profile');
var url = "database/fetch_data.php?query=";
var query = "Select * from question where userId=2";
url += query;
$http.get(url).success(function(questionData) {
$scope.questions = questionData;
console.log($scope.questions);
});
};
<div>
User Profile
<br/>Question Posted
<br/>
<input ng-model="query.title" id="value" type="text" placeholder="Search by Title..." ">
<div>
<ul>
<li ng-repeat="question in questions | filter: query ">
{{question.title}}
</li>
</ul>
</div>
<br/>
</div>
You need to move your $location.path('/profile') inside your http request. Remember that a http request is async call. You should redirect after getting the data not before.
$scope.signIn = function(data) {
var url = "database/fetch_data.php?query=";
var query = "Select * from question where userId=2";
url += query;
$http.get(url).success(function(questionData) {
$scope.questions = questionData;
console.log($scope.questions);
$location.path('/profile');
});
};
If you're redirecting to another route with a completely separate scope you will lose any scope you're setting in the success handling.
From what I'm reading you're clicking a button to do an action. After that action you're redirecting to another page with a separate controller and trying to persist the data.
Unfortunately, Angular hasn't figured out a great way to do this. The easiest way to persist data through controllers and scope is to create a service that will store it in one controller and grab it in another controller.
For instance:
$scope.signIn = function(data) {
var url = "database/fetch_data.php?query=";
var query = "Select * from question where userId=2";
url += query;
$http.get(url).success(function(questionData) {
$location.path('/profile');
storageService.store("question", questiondata)
});
};
Your new factory to persist data through:
angular.module('moduleName').factory('storageService', [
function () {
return {
store: function (key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
get: function(key) {
return JSON.parse(localStorage.getItem(key));
},
remove: function(key) {
localStorage.removeItem(key);
}
}
}
]);
Other controller to access data:
$scope.question = storageService.get("question");
// remove localstorage after you've grabbed it in the new controller
storageService.remove("question");
An alternative to doing the somewhat 'hacky' way of using localStorage to persist data through controllers is to use ui-router and have a resolve on the route you're redirecting to.
For instance:
$scope.signIn = function(data) {
$state.go('profile');
};
In your route file:
.state('profile', {
url: '/profile'
controller: profileControllerName,
templateUrl: 'profileHtmlTemplate.html',
resolve: {
'questions': [function() {
var url = "database/fetch_data.php?query=";
var query = "Select * from question where userId=2";
url += query;
$http.get(url).success(function(res) {
return res.data;
});
}]
}
}
In your profile controller:
Inject your 'questions' resolve into your controller and assign `$scope.question = questions;
This will make the HTTP call as soon as you click the route, return the data if successful, then render the page. It will NOT render the page if the resolve does not return success. This will ensure your data will be loaded before you load the page that depends on that data.
I would highly recommend using services to hold your HTTP calls for specific parts of your application. If you have a GET questions, POST question, PUT question. I would create a questionService and make all my HTTP methods there so you don't have to clutter your routes. You would only have to call:
.state('profile', {
url: '/profile'
controller: profileControllerName,
templateUrl: 'profileHtmlTemplate.html',
resolve: {
'questions': [function() {
return questionService.getQuestions(id).then(function(res) {
return res.data;
})
}]
}
}

DOM loading before the promise defer completes

I have a model that I am using to hold my data in angular:
var FuelProcessingModel = function (carrierService) {
this.myArray = [];
};
That model has an array of MyObjects that I get from the DB:
var MyObject = function () {
//stuff
}
I update this using a REST call:
$scope.add = function () {
var myObject = new MyObject();
fuelProcessingService.add(myObject).then(function(result) {
$scope.model.MyObjects.push(result.data);
});
};
Here you can see I want to push the result to the array so that it is added on the screen. But the problem is the DOM loads before that happens.
service to hit the Server:
this.add = function (myObject) {
var deferred = $q.defer();
$http({
method: "POST",
url: "theServer",
data: myObject,
}).success(function (data) {
deferred.resolve(data);
});
return deferred.promise;
}
The REST service adds to the Database and then returns the updated object
I thought that $q.defer() would cause the DOM to wait to load until the result is returned so that the newly added item shows up.
What am I missing?
UPDATE
I tried doing this but I still have the same problem. The push call is not being called before the DOM loads the page, so the user never sees the added item.
this.add = function (myObject) {
return $http({
method: "POST",
url: "theServer",
data: myObject,
}).success(function (data) {
return data;
});
Html that creates and uses the object
<div ng-repeat="myObject in model.MyObjects" style="margin-bottom: 2%">
//A table of myObjectData
<button class="btn btn-success" ng-click="add()">Add My Object</button>
</div>
Here is what happens in Angular.
Initial html is rendered (also called template html).
Angular boostraps, and then does the necessary binding and the view is updated.
Angular watches for changes and if there is a change in data, view is updated automatically.
When you bind the array to the view the, it renders what is available. When you add some new element to the array later the view should automatically update. You do not need to bother with creating your own promise just do
return $http...

Passing current tab url to server in angular resources fetch

I am trying to send the current tab url to the resource service { in param } .
but the global tablUrl is not having any value at
var url = "http://[localhost]/getProfile?domain="+tabUrl
but getting logged corrent at :
console.log(tabUrl);
this is my code :
var tabUrl;
angular.module('jsonService', ['ngResource'])
.factory('JsonService', function($resource) {
chrome.tabs.getSelected(null, function(tab) {
tabUrl = tab.url;
console.log(tabUrl);
});
var url = "http://[localhost]/getProfile?domain="+tabUrl
return $resource(url,{}, {
list : {
method : 'GET',
cache : true
}
});
});
template binding :
<body ng-controller="extensionCtrl">
this is controller :
app.controller('extensionCtrl', function($scope , JsonService) {
JsonService.get(function(data){
$scope.data = data;
});
});
First:
Please, don't use the deprecated chrome.tabs.getSelected. Use chrome.tabs.query instead.
Second:
chrome.tabs.getSelected/chrome.tabs.query are asynchronous. This means that execution continues while they do some work in the background and the specified callback is called when they are done.
So, in a case like this:
line 1: chrome.tabs.getSelected(null, funkyCallback);
line 2: var url = ...
line 3: return $resource(...);
...a possible (and very probable) order of execution is:
1. chrome.tabs.getSelected (starts retrieving the active tab in the background)
2. line 2 gets executed (at this time 'tabURL' is not set yet)
3. line 3 gets executed (returning something)
4. Once the the active tab is retrieved, 'funkyCallback' is called
(setting 'tabURL' after it is too late).
When using asynchronous APIs (such as most of the chrome.* APIs), you have to change the whole logic of your scripts to be in line with the asynchronous nature of the API calls.
E.g., you could achieve the same result like this:
HTML:
<html ng-app="jsonService">
...
<body ng-controller="extensionCtrl">
<p>{{jsonData}}</p>
...
JS:
var app = angular.module("jsonService", ["ngResource"]);
app.factory("JsonFactory", function($resource) {
var url = "http://localhost/getProfile?domain=:tabUrl";
var retObj = $resource(url, {}, {
list: {
method: "GET",
cache: true
}
});
return retObj;
});
app.controller("extensionCtrl", function($q, $rootScope, JsonFactory) {
chrome.tabs.query({ active: true }, function(tabs) {
JsonFactory.list({ tabUrl: tabs[0].url }, function(data) {
// On success...
$rootScope.jsonData = data;
}, function(data) {
// On error...
$rootScope.jsonData = "Error using JsonFactory.list(...) !";
});
});
});
See, also, this short demo that does something similarly asynchronous

Resources