update scope variable on submit and update view in angular js - javascript

I'm creating my first web app in angularjs and can't get the page to update with new values once the user submits text/numbers in an input box.
I'm using Java8, MongoDB, angularJS and twitter bootstrap
HTML:
<td>
<input type="text" class="form-control" placeholder="enter bugnumber" data-ng-model="auditdata.newbugnumber">
<h4>Bug Numbers</h4>
{{a}}
</td>
<td>
<button type="submit" data-ng-click="add(auditdata)" class="btn btn-danger" >Save</button>
</td>
In the HTML above i take input from user in ng-model=auditadata.newbugnumber but on server side it sill gets update in the bugnumber filed. The newbugnumber field is acting like a temp variable which is just used to send the new data to the server. The reason for using the temp variable is to avoid two way binding in angularjs.
I tried using $apply(), $watch and digest in the JS below but can't get the value to be updated in the view. The only way the data gets update in view is when i reload the page which is not an option
app.controller('listCtrl', function ($scope, $http,$route) {
$scope.isCollapsed = true;
$http.get('/api/v1/result').success(function (data) {
$scope.audit = data;
}).error(function (data, status) {
console.log('Error ' + data);
})
$scope.add= function(bugInfo) {
$http.post('/api/v1/result/updateBug', bugInfo).success(function (data) {
bugInfo.newbugnumber='';
console.log('audit data updated');
}).error(function (data, status) {
console.log('Error ' + data);
}
};
});
Update function on server side
public void updateAuditData(String body) {
Result updateAudit = new Gson().fromJson(body, Result.class);
audit.update(
new BasicDBObject("_id", new ObjectId(updateAudit.getId())),
new BasicDBObject("$push", new BasicDBObject().append("bugnumber",
updateAudit.getNewbugnumber())));
}
how bugnumber filed in collection looks like
> db.audit.find({"_id" : ObjectId("5696e2cee4b05e970b5a0a68")})
{
"bugnumber" : [
"789000",
"1212"
]
}

Based on your comment, do the following:
Place all your $http handling i a service or factory. This is good practice and makes reuse and testing easier
app.factory('AuditService', function($http) {
var get = function() {
return $http.get("/api/...") // returns a promise
};
var add = function() {
return $http.post("/api/...") // returns a promise
};
return {
get: get,
add: add
}
});
And then in your controller
// note the import of AuditService
app.controller('listCtrl', function ($scope, $http,$route, AuditService) {
// other code here
// If insert is successful, then update $scope by calling the newly updated collection.
// (this is chaining the events using then())
$scope.add = function(bugInfo) {
AuditService.add().then(
function(){ // successcallback
AuditService.get().then(
function(data){ // success
$scope.audit = data;
},
function(){ // error
console.log('error reading data ' + error)
})
},
function(error){ // errorcallback
console.log('error inserting data ' + error)
})
});
The same approach can be applied to all CRUD operations

Related

How to pass data to angular async function from ng-click

I am trying to use an async function inside a service (so I can acces it from many controllers) and fill an array when I click on an element on the page.
As it is now, the async function will load right after page load. Here is the function ;
routesApp.factory('angRoutes', function($http) {
var angRoutes = {
async: function() { <----------- How to call this function with params
var data = $.param({
query: 'top' <------------------- HOW TO PASS SOMETHING HERE
}); <-- I need to use ng-click="update('top')"
var config = {
headers : {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
}
}
var promise = $http.post('../ajax-php.php', data, config)
.success(function (data, status, headers, config) {
$('#ajaxLoader').hide(); /// hiding the loading gif
return data;
})
.error(function (data, status, header, config) {
return data = "Data: " + data +
"<hr />status: " + status +
"<hr />headers: " + header +
"<hr />config: " + config;
});
return promise;
}
};
return angRoutes;
});
And the controller ;
routesApp.controller('topRoutesCtrl', function topRoutesCtrl($scope,$http, angRoutes) {
angRoutes.async().then(function(data) {
$scope.angRoutes = data;
console.log(data);
});
});
I want to update my $scope.angRoutes using the same kind of async function but I need to pass data to the function. For Exemple ;
<div ng-controller="navRoutesCtrl" class="c prise">c
<img ng-click="update()" data-query="top" class= "priseimg" src="../images/prises/c-s.png">
<div class="arrow-all arrow-c">
TOP ROUTES
</div>
</div>
I use to do it with jquery and I would get the query param for the post from de data-query attribute.
Now I want to turn this into ;
<div ng-controller="navRoutesCtrl" class="c prise">
<img ng-click="update('top')" class= "priseimg" src="../images/prises/c-s.png">
<div class="arrow-all arrow-c">
TOP ROUTES
</div>
</div>
How do I change my service async function and set the .post query to what ever I pass to the update('query').
How do I need to rewrite the function so I can use it with ng-click
I want the exact same thing to happens but from a diffrent controller and a controller that will call the async function with a diffrent query.
Basically I need to call for an async $http function (result come from database and are sent as Json) from a ng-click with at least one param for the query.

Angular use variable before running rest of the code

I know the problem is the order of execution, the scope fires async and loads the data after the rest of the code has been processed. I have a httpGET that gets the information from a web service and it's inside my Facebook share function when the user clicks the Facebook share button. But i tried adding a watch and .then with return promise in the httpget but both i could not get to work.
So the idea is the following: I have an CDImage that the user shares on facebook and i have another directory that holds promotional images for that same CD, not all of them so the httpGET checks if the promotionCDid exists if it exists the variable CDImage should be updated with the CDPromotionalURL instead of the standard url is get from CDImage so the user shares the Promotional image instead of the default CD cover.
So far the problem is that the CDImage does not change directly and console.log(CDImage) displays the CDCover the first time and when you click the button after several seconds the CDImage shows the CDPRomotionURL image.
var CDPromotionalImageUrl = "EMPTY";
$('#facebookshare').click(function () {
$scope.GetCDdata = function () {
$http({
method: 'Get',
url: "/GetCDPromotionImage?id_CD=" + promotionCDId,
})
.success(function (data, status, headers, config) {
$scope.CDdata = data;
CDPromotionalImage = $scope.CDdata[0].filename
$scope.CDPromotionalImageUrl = "https://website.com/" + CDPromotionalImage
})
.error(function (data, status, headers, config) {
$scope.message = 'Unexpected Error';
});
};
if (CDPromotionalImageUrl == "EMPTY") {
CDImage = CDImage;
} else {
CDImage = CDPromotionalImageUrl;
}
console.log(CDImage)
var $this = $(this);
var urlShare = (window.location.href);
var obj = {
method: 'share',
href: (urlShare),
picture: (CDImage),
title: 'The ' + CDName,
caption: "As heard on website.com",
description:(description)
};
function callback(response) {
//alert("Post ID: " + response['post_id']);
}
FB.ui(obj, callback);
});
$scope.GetCDdata();

Filtering data in one table by clicking on a row in another datatables AngularJS

I have two AngularJS DataTables displayed. First one with counties and second with towns. So, what I need to do is filter data in second table by clicking a row in first table. For example, when I click on row with Orange County in first table, in second should be filtered only towns from that county, not other ones. Any suggestions and pointers on how to do that would be highly appreciated.
This is my code for displaying tables:
<div>
<datatables-lazy columns="countiesColumns" options="countiesOptions" class="grid"></datatables-lazy>
</div>
$scope.countiesColumns = [
DTColumnBuilder.newColumn('name').withTitle(localizationService.getLabel('ime')),
DTColumnBuilder.newColumn('code').withTitle(localizationService.getLabel('kod')),
DTColumnBuilder.newColumn(null).withTitle('Akcije').notSortable().renderWith(actionsHtml)
];
$scope.countiesOptions = DTOptionsBuilder.fromFnPromise(function () { return settingsService.getCounties(); })
.withPaginationType('full_numbers')
.withOption('createdRow', createdRow);
function createdRow(row) {
$compile(angular.element(row).contents())($scope);
}
Here are services for getting data from database:
getCounties: function () {
var deferred = $q.defer();
$http.get(applicationSettings.humanResourcesAPIUrl + 'settings/getCountyList')
.success(function (response) { deferred.resolve(response); })
.error(function (err, status) { deferred.reject(err); });
return deferred.promise;
},
getCounty: function (id) {
var deferred = $q.defer();
$http.get(applicationSettings.humanResourcesAPIUrl + 'settings/getCounty?countyId=' + id)
.success(function (response) { deferred.resolve(response); })
.error(function (err, status) { deferred.reject(err); });
return deferred.promise;
},
Everything is same for second datatable Towns.
You would need to setup an ng-click on the first table so that it assigns a value of the selected town to a variable via a function.
eg. ng-click="setTown(town)"
$scope.activeTown = "";
$scope.setTown = function(town) {
$scope.activeTown = town;
}
Then in the second table, all you would need is to filter on this activeTown variable.
<tr ng-repeat="place in places | filter:activeTown">
Here is an example
http://plnkr.co/edit/os5X3ugAZOVC5v1zn0tF?p=preview

Knockout.js Update ViewModel on button click

Well, that's not the best situation description... Anyway, I'm trying to update my ViewModel but it's not working. By default I'm getting data from controller function and by button click - from another function in same contoller, but ViewModel contain only data received after first ViewModel initialization.
<script>
function viewModel () {
var self = this;
self.currentPage = ko.observable();
self.pageSize = ko.observable(10);
self.currentPageIndex = ko.observable(0);
self.salesdata = ko.observableArray();
self.newdata = ko.observable();
self.currentPage = ko.computed(function () {
var pagesize = parseInt(self.pageSize(), 10),
startIndex = pagesize * self.currentPageIndex(),
endIndex = startIndex + pagesize;
return self.salesdata.slice(startIndex, endIndex);
});
self.nextPage = function () {
if (((self.currentPageIndex() + 1) * self.pageSize()) < self.salesdata().length) {
self.currentPageIndex(self.currentPageIndex() + 1);
}
else {
self.currentPageIndex(0);
}
}
self.previousPage = function () {
if (self.currentPageIndex() > 0) {
self.currentPageIndex(self.currentPageIndex() - 1);
}
else {
self.currentPageIndex((Math.ceil(self.salesdata().length / self.pageSize())) - 1);
}
}
//Here I'm trying to update ViewModel
self.request = function (uri) {
$.ajax({
url: uri,
contentType: 'application/json',
data: [],
type: 'GET',
cache: false,
success: function (data) {
ko.mapping.fromJS(data.$values, {}, self.salesdata);
}
});
}
}
$(document).ready(function () {
$.ajax({
url: "/api/sales",
type: "GET",
cache: false,
}).done(function (data) {
var vm = new viewModel();
vm.salesdata(data.$values);
ko.applyBindings(vm);
}).error(function (xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
});
//Here i'm calling for ViewModel update
$(".btn-default").click(function () {
days = $(this).val();
var uri = "/api/sales?days=" + days;
new viewModel().request(uri);
});
});
</script>
UPDATE.
I chaged block of code where I'm getting new data to be as follow:
self.request = function (uri) {
$.getJSON(uri, function (data) {
ko.mapping.fromJS(data.$values, {}, viewModel);
});
}
Unfortunately this is not working as well. Here is no any JS errors, controller return proper portion of updated data.
I'm new to all of this, but if I'm reading your code correctly, you are calling the request function on a new instance of the view model and not the one that was bound to the html document. You need to make the request call on the view model that you created after the initial get call completed.
Update:
Sorry, I should have been more specific about the code I was referring to. At the end of your code block you have the following code:
$(".btn-default").click(function () {
days = $(this).val();
var uri = "/api/sales?days=" + days;
new viewModel().request(uri);
});
In this code, it appears that each time the default button is clicked, a new view model is created and the request function is called on that view model.
In the document ready function where you are defining what happens after the sales data is loaded, you have the following code which is what creates the view model that the html document is actually bound to:
var vm = new viewModel();
vm.salesdata(data.$values);
ko.applyBindings(vm);
Nothing ever calls the request function on this view model. I wonder if what you really want is to somehow bind the request function in this view model to the default button.
I would try updating the viewmodel salesdata observable, by giving context: self and using the following success method:
self.request = function (uri) {
$.ajax({
url: uri,
contentType: 'application/json',
context: self,
data: [],
type: 'GET',
cache: false,
success: function (data) {
this.salesdata(data.$values);
}
});
}
EDIT:
I can see you attached a click event with jQuery.
You should use knockout clck binding instead:
<button data-bind="click: clickEvent" value="1">Click me!</button>
And in the viewmodel
clickEvent: function (data, event) {
days = event.target.value;
var uri = "/api/sales?days=" + days;
data.request(uri);
}
This way you can retrieve your viewmodel instead of creating a new one as you did with new viewModel().request(uri);
For more on click binding see http://knockoutjs.com/documentation/click-binding.html
Building slightly on the answer from #brader24 here:
In your update button's click event, you use this line of code:
new viewModel().request(uri);
What that is doing is creating a new viewModel (separate from the one that you already have instantiated and have applied bindings for) and filling it's observable array with data via your request function. It isn't affecting your original viewModel at all (the one that has it's bindings applied on the DOM!). So you won't see any errors, but you also won't see anything happening on the page because all you did was create a new viewModel in memory, fill it with data, and do nothing with it.
Try this code (everything in your viewModel function looks fine).
$(document).ready(function () {
var vm = new viewModel(); // declare (and instantiate) your view model variable outside the context of the $.ajax call so that we have access to it in the click binding
$.ajax({
url: "/api/sales",
type: "GET",
cache: false,
}).done(function (data) {
vm.salesdata(data.$values);
ko.applyBindings(vm);
}).error(function (xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
});
//Here i'm calling for ViewModel update
$(".btn-default").click(function () {
days = $(this).val();
var uri = "/api/sales?days=" + days;
vm.request(uri); // don't use a new instance of a view model - use the one you have already instantiated
});
});
Using a Knockout click binding instead of attaching a click event handler using jQuery is usually the recommended route, but it is not necessary - so your existing code (with the modifications above) should work fine. For more info on that, see Using unobtrusive event handlers in the Knockout docs
Well. Final solution based on #GoTo answer is:
Here is the way to call function in viewmodel via click databind.
<button type="button" class="btn btn-default" id="7" value="7" data-bind="click: getDays.bind($data, '7')">7</button>
Here is the function. As you can see I'm calling self.salesdata instead of viewModel. This solution is working fine but somehow now I have problem with data format that is binded this way -
<td data-bind="text: moment($data.whensold).format('DD.MM', 'ru')"></td>.
self.getDays = function (days) {
var uri = "/api/sales?days=" + days;
$.getJSON(uri, function (data) {
ko.mapping.fromJS(data.$values, {}, self.salesdata);
});
}

ng-repeat update after build

I have a list of events that gets build from a JSON call to my server. The list gets parsed through ng-repeat. I want to implement a like button for the event and if its liked, replace that with unlike.
<a ng-show='event.liked==null' ng-click="like(event.event_id)">Like</a>
<a ng-show='event.liked!=null' ng-click="unLike(event.event_id)">Unlike</a>
Everything works perfectly except I have to refresh the feed to show "Unlike". Is there a way I can update the specific list item at the index that was liked once clicked without the need to refresh.
Please let me know if you need more information. Thanks!
edit: adding like function & unlike function. All it does is send request to my server to like or unlike a specific event with the event_id and user token.
$scope.like = function (event_id) {
var url = www.server.com?type=unlike&event_id=...
$http.post(url).success(function (data) {
console.log('success like');
//I want it to update my index here
}).error(function (data) {
console.log('fail like');
});
};
$scope.unLike = function (event_id) {
var url = www.server.com?type=unlike&event_id=...
$http.post(url).success(function (data) {
console.log('success unlike');
//I want it to update my index here
}).error(function (data) {
console.log('fail unlike');
});
};
Instead of passing in the event_id, pass the object to the like and unLike functions and update the object in success handler.
HTML
<a ng-hide='event.liked' ng-click="like(event)">Like</a>
<a ng-show='event.liked' ng-click="unLike(event)">Unlike</a>
Controller
$scope.like = function(event) {
var url = 'www.server.com?type=unlike&event_id=' + event.event_id;
$http.post(url).success(function (data) {
event.liked = true;
console.log('success like');
}).error(function (data) {
console.log('fail like');
});
};
$scope.unLike = function(event) {
var url = 'www.server.com?type=unlike&event_id=' + event.event_id;
$http.post(url).success(function (data) {
event.liked = null;
console.log('success unlike');
}).error(function (data) {
console.log('fail unlike');
});
};

Categories

Resources