I'm building a calendar inspired by this example http://plnkr.co/edit/pIDltQRV6TQGD4KQYnj7?p=preview and adding $save to add new events using RESTful server connection.
I'm trying to find a way to make the calendar show the new events when they are $saved without manually refreshing the browser.
My attempt to get this to work was to add (or remove) the event data to the event array (gDataService.events). Although it does change the content of the array, the change is not shown in the calendar. (e.g., if I change the date of the event, the event won't move to the new date.)
Can someone point me in the right direction? Thanks!
HTML
<div ui-calendar="uiConfig.calendar" class="span8 calendar" ng-model="eventSources"></div>
Controller1 ... This saves new event.
$scope.ok = function () {
$scope.entry = new calFactory();
$scope.entry.data = data
$scope.entry.$save( function(){
// data saved. do something here.
toaster.pop('success','Message','Update successfully completed.');
});
};
Controller2 ... Main controller that defines eventSource
myApp.controller("MainCtrl", function($scope,$compile,uiCalendarConfig, calFactory,eventFactory, gDataService) {
gDataService.events = function(start, end, callback) {
var d = new Date(start);
var events;
events = calFactory.query({
start: start,
end: end
});
events.$promise.then(function(value){
gDataService.events = events;
//have to call the callback as well to keep the calendar happy
callback(gDataService.events);
$scope.statusTxt = $scope.statusTxt + " ... Event loading completed at " + moment(new Date()).format("HH:mm:ss");
}
);
};
/* event sources array*/
$scope.eventSources = [gDataService.events]; /*, $scope.eventSource, $scope.eventsF*/
})
Factory
myApp.factory("calFactory",['$resource','$filter', function($resource, $filter) {
return $resource("/griddata/", {}, {
get: {
method: 'GET'
},
save: {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
transformRequest: function(data, headersGetter) {
data = angular.toJson(data);
data = $.parseJSON(data);
return $.param(data.data);
}
}
});
}]);
gDataService ... This stores event data and make it available to other parts of the program
myApp.factory("gDataService", function ($rootScope, calFactory) {
var service = {};
service.events = [];
service.addData = function(object, no_broadcast) {
this.events.push({
__id: object.task_id, title: object.task, start: object.duedates,
backgroundColor: bColor, textColor: bTextColor, borderColor: bColor
});
if (!no_broadcast) {$rootScope.$broadcast("gDataUpdated")};
};
service.removeData = function(task_id, no_broadcast) {
var arr_index = _.findIndex(this.events, {'__id': task_id});
this.events.splice(arr_index, 1);
if (!no_broadcast) {$rootScope.$broadcast("gDataUpdated")};
};
return service;
});
Nobody would answer to this much sophisticated question.
You have some typos:
Missed closing with ";", "]"...
What's inside 'function calFactory'? What is '$broadcast'?
Why do you put '$' leading the JavaScript object? Is this meaning "private var"?
"if (!no_broadcast) ..." is not coding, but commenting.
In "$scope.entry.$save( function(){",
Why entry doesn't have '$', but scope and save have it?
Here is the answer from chris-rock https://github.com/angular-ui/ui-calendar/issues/200.
Changed scope.init in calendar.js like this:
scope.init = function(){
calendar.fullCalendar(options);
window.calendar = calendar; /// This is the key
};
Now I can add or remove events dynamically using window.calendar.fullCalendar('removeEvents' or 'renderEvent')!!
Here is how I changed my code.
gDataService
service.addData = function(object, no_broadcast) {
//add additional project
this.added_event = {
__id: object.task_id, title: object.task, start: object.duedates,
backgroundColor: bColor, textColor: bTextColor, borderColor: bColor
};
this.events.push(this.added_event);
if (!no_broadcast) {$rootScope.$broadcast("gDataUpdated")};
};
service.removeData = function(_id, no_broadcast) {
var arr_index = _.findIndex(this.events, {'_id': _id});
this.delete_id = _id;
this.events.splice(arr_index, 1);
if (!no_broadcast) {$rootScope.$broadcast("gDataUpdated")};
};
return service;
Controller
$scope.$on('gDataUpdated', function(){
if (gDataService.delete_id) {
window.calendar.fullCalendar('removeEvents',gDataService.delete_id); // This removes this event from the calendar
gDataService.delete_id = null;
};
if (gDataService.added_event) {
window.calendar.fullCalendar('renderEvent',gDataService.added_event,false); // This adds this event to the calendar
gDataService.added_event = null;
};
Related
I need to change the order of scope, save but me back an error that save() is not a function.
I'm using restangular to create the objects.
The function is triggered Onsort, I tried using http, but also gives me error.
$scope.onChange = function() {
ApiRestangular.all($scope.section).getList($scope.query).then(function(res){
$scope.items = res;
order = ApiRestangular.copy(res);
console.log(order);
$scope.sortOptions = {
animation : 150,
onSort: function(){
order.put().then(function(){
toast.msgToast($scope.section+ ' ...Ordem atualizada!');
});
}
};
});
};
I'm having a problem re rendering a simple collection in backbone, the render event is never fired from the listeners... I'm not sure of where is the mistake, could please someone help me?
File with models:
window.MetricDevice = Backbone.Model.extend({
defaults: {
ip: null,
framesReceived: null,
framesOutOfOrder: null,
framesLost: null
}
});
window.MetricDevicesCollection = Backbone.Collection.extend({
model: MetricDevice,
value: null,
url: function(){
return hackBase + "/wm/iptv/metric/devices/json";
},
initialize:function () {
this.fetch({ reset: true });
console.log("data fetched");
},
});
Render page:
window.MetricItemView = Backbone.View.extend({
events: {
"click input[type=button]" : "removeDevice",
},
initialize:function(){
this.template = _.template(tpl.get('metric-devices-item'));
this.render();
},
removeDevice:function(){
$.ajax({
url:hackBase + '/wm/iptv/metric/disable/' + this.model.get("ip") + '/0/json',
dataType:"json",
success:function (data) {
if ( data.return == 1 ){
alert(data.error);
}else{
alert("Metric disabled in " + this.model.get("ip"));
}
},
});
},
render:function(){
var ip = this.model.get("ip");
console.log("rendering item in view " + ip);
},
});
window.MetricView = Backbone.View.extend({
events: {
"click input[type=button]" : "add",
"click input[type=img]" : "updateAll",
},
clicked:function(e){
},
updateAll:function(e){
this.render();
},
initialize:function () {
this.template = _.template(tpl.get('metric-devices-list'));
this.model.bind("change", function(){
console.log("metricView data change detected");
this.render();
});
this.model.bind("reset", this.render());
},
add:function(e){
if($(e.currentTarget).attr("name") == "add" ){
var ip = document.getElementById('vaddress').value;
var threshold = document.getElementById('vthreshold').value;
$.ajax({
url:hackBase + '/wm/iptv/metric/enable/' + ip + '/' + threshold + '/json',
dataType:"json",
success:function (data) {
if ( data.return == 1 ){
alert(data.error);
}else{
alert("Metric enabled in device");
}
},
});
}else if($(e.currentTarget).attr("name") == "cancel"){
document.getElementById('vaddress').value = "";
document.getElementById('vthreshold').value = "";
}
},
render:function (eventName) {
$(this.el).html(this.template());
var list = $(this.el).find('#tableData');
console.log("On render!");
var subviews = [];
console.log("looping on models");
_.each(this.model.models, function (sw) {
console.log("model loop " + sw.get("ip"));
var m = new MetricItemView({model:sw, tagName: 'tbody', el: $(this.el).find('#tableData')});
list.append(m.template(sw.toJSON()));
}, this);
return this;
},
});
The problem is that the render method in MetricView is called just when the page is loaded for the first time, and after this I've the impression that the JSON stay cached, and the content just change if I close the browser clean the cache and run again...
The console output is:
On render! metricView.js:86
looping on models metricView.js:88
model loop 10.0.0.1 metricView.js:92
rendering item in view 10.0.0.1 metricView.js:26
And I'm instantiating MetricView like this
var metricdevices = new MetricDevicesCollection();
$('#content').html(new MetricView({model:metricdevices}).render().el);
Am i forgetting something?
The problem you are facing is that you are not using Backbone models and collection at their full potential. When you are calling manual $.ajax(...), no Backbone event will fire. Here are a couple suggestions that would make your code integrate with Backbone.
First, you should instantiate your view with the proper reserved keyword: collection
var metricdevices = new MetricDevicesCollection();
$('#content').html(new MetricView({ collection : metricdevices }).render().el);
Backbone events in collection are intended to work with precise REST API practices. A Model that belongs to a Collection will inherit it's url parameter. It expects your REST API to map to the following scheme:
model.save() --> model.id is present ? PUT collection.url/model.id : POST collection.url
model.delete() --> DELETE collection.url/model.id
model.fetch() --> GET collection.url/model.id
The idea is that you can manipulate models individually and use collection to fetch all the relevant models when needed. Your API does not seem to be adapted for that kind of workflow.
A monky patch that would keep your data updated is to trigger a fetch of the collection when an operation succeeds.
var collection = this.collection;
$.ajax({
//...
success: {
collection.fetch();
}
}
Since you are already listening to the Backbone events, it will trigger the reset event and render the view. Note that this is not a good way of doing things. What you should do is refactor your server access points to conform to standard good REST practice if you have access to it. If you don't, use proper Object Oriented patterns and implement model behavior in the model. For example:
window.MetricDevice = Backbone.Model.extend({
defaults: {
ip: null,
framesReceived: null,
framesOutOfOrder: null,
framesLost: null
},
enable : function() {
var device = this,
ip = this.ip,
treshold = this.treshold;
$.ajax({
url:hackBase + '/wm/iptv/metric/enable/' + ip + '/' + threshold + '/json',
dataType:"json",
success:function (data) {
if ( data.return == 1 ){
alert(data.error);
} else {
device.trigger('change');
alert("Metric enabled in device");
}
}
});
return this;
}
});
And then you can properly call the object from a view:
var ip = document.getElementById('vaddress').value;
var threshold = document.getElementById('vthreshold').value;
var metricDevice = new MetricDevice({ ip : ip, treshold : treshold });
this.collection.add(metricDevice.enable());
I'm trying to get the following findTimelineEntries function inside an Angular controller executing after saveInterview finishes:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId}, function() {
$scope.findTimelineEntries();
});
};
The save action adds or edits data that also is part of the timeline entries and therefore I want the updated timeline entries to be shown.
First I tried changing it to this:
$scope.saveInterview = function() {
var functionReturned = $scope.interviewForm.$save({employeeId: $scope.employeeId});
if (functionReturned) {
$scope.findTimelineEntries();
}
};
Later to this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
$scope.saveInterview.done(function(result) {
$scope.findTimelineEntries();
});
And finaly I found some info about promises so I tried this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
var promise = $scope.saveInterview();
promise.done(function() {
$scope.findTimelineEntries();
});
But somehow the fact that it does work this way according to http://nurkiewicz.blogspot.nl/2013/03/promises-and-deferred-objects-in-jquery.html, doesn't mean that I can use the same method on those $scope.someFuntcion = function() functions :-S
Here is a sample using promises. First you'll need to include $q to your controller.
$scope.saveInterview = function() {
var d = $q.defer();
// do something that probably has a callback.
$scope.interviewForm.$save({employeeId: $scope.employeeId}).then(function(data) {
d.resolve(data); // assuming data is something you want to return. It could be true or anything you want.
});
return d.promise;
}
In the backbone.js, for the purpose of filtering the data, i am fetching by click the element.
and i am saving the collection as newcollection. but i unable to get any data.
what is wrong with my code...
my code :
taskListPhraseI.collection = Backbone.Collection.extend({ // collection fetching
model:taskListPhraseI.model,
url : 'data/data.json',
});
taskListPhraseI.allView = Backbone.View.extend({
el:$('.boardHolder'),
events:{
'click span.green' : 'filterIt'
},
initialize:function(){
var that = this;_.bindAll(this);
this.collection = new taskListPhraseI.collection(); //initial stage i am fetching
this.collection.fetch({success:that.render});
this.on('change:filterType', this.setNewType); //on click trigger my custom method to get the collection again
//this.on('reset:filterType', this.setNewType);
},
setNewType:function(){
var newCollection = new taskListPhraseI.collection(); // my custom collection
newCollection.fetch(); // fetching
this.collection.reset(newCollection,{ silent: true }) // triggering rest
var filterType = this.filterType,
filtered = _.filter(this.collection.models, function (item) {
return item.get("dueDays") === filterType;
});
console.log(newCollection.models); // not working... why?
console.log(this.collection.models);// works
this.collection.reset(filtered);
},
or the way i am doing wrong.. to filter the collection
any one guide me a correct way of process...
thanks in advance
fetch is async. Execute your code after collection will be fetched
newCollection.fetch({context:this}).done(function() {
// your code
})
Plus this is not correct reset method usage:
this.collection.reset(newCollection,{ silent: true })
Use this way:
this.collection.reset(newCollection.toJSON(), {silent:true})
EDIT (Added example)
HTML
<button>change filter</button>
JS
var url1 = 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=vpetrychuk&count=9'
var url2 = 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=derickbailey&count=9'
var collection = new (Backbone.Collection.extend({
url : url1,
resetWithFilter : function(key, value) {
var query = {};
query[key] = value;
this.reset(this.where(query));
}
}));
// fetch initial data
collection.fetch({dataType:'jsonp'});
$(':button').on('click', function() {
// change url and fetch another data
collection.url = url2;
collection.fetch({dataType:'jsonp'}).done(function(response) {
console.log('items count before filter:', collection.length);
// now reset collection with selected filter
collection.resetWithFilter('id_str', '294429621640912896');
console.log('items count after filter:', collection.length)
});
});
Fiddle: http://jsfiddle.net/vpetrychuk/N4ZKm/
What is the recommended way to connect to server data sources in AngularJS without using $resource.
The $resource has many limitations such as:
Not using proper futures
Not being flexible enough
There are cases when $resource may not be appropriate when talking to backend. This shows how to set up $resource like behavior without using resource.
angular.module('myApp').factory('Book', function($http) {
// Book is a class which we can use for retrieving and
// updating data on the server
var Book = function(data) {
angular.extend(this, data);
}
// a static method to retrieve Book by ID
Book.get = function(id) {
return $http.get('/Book/' + id).then(function(response) {
return new Book(response.data);
});
};
// an instance method to create a new Book
Book.prototype.create = function() {
var book = this;
return $http.post('/Book/', book).then(function(response) {
book.id = response.data.id;
return book;
});
}
return Book;
});
Then inside your controller you can:
var AppController = function(Book) {
// to create a Book
var book = new Book();
book.name = 'AngularJS in nutshell';
book.create();
// to retrieve a book
var bookPromise = Book.get(123);
bookPromise.then(function(b) {
book = b;
});
};
I recommend that you use $resource.
It may support (url override) in next version of Angularjs.
Then you will be able to code like this:
// need to register as a serviceName
$resource('/user/:userId', {userId:'#id'}, {
'customActionName': {
url:'/user/someURI'
method:'GET',
params: {
param1: '....',
param2: '....',
}
},
....
});
And return callbacks can be handled in ctrl scope like this.
// ctrl scope
serviceName.customActionName ({
paramName:'param',
...
},
function (resp) {
//handle return callback
},
function (error) {
//handler error callback
});
Probably you can handle code on higher abstraction level.