Fullcalendar 3 change draggable events when changing week - javascript

I work with fullcalendar 3 with laravel.
The draggable events are courses and should be different every week. I want to dynamically change the courses when I change the week in the calendar.
At the moment in the controller I have a function that give me every course:
public function index()
{
$corsi = Corso::select('titolo', 'descrizione','ore_45esimi')->get();
return view('lezione.calendario_lezioni')->with(array('corsi' => $corsi));
}
then in the view I display the events using a div:
<div id="external-events">
#foreach($corsi as $corso)
<div class="external-event bg-formazione">{{$corso->titolo }}
<br>
<div style="font-size: small">{{$corso->descrizione }} </div>
<input id="ore" type="hidden" value="{{$corso->ore_45esimi }}">
</div>
#endforeach
</div>
And in the Fullcalendar js file I have a function to make the courses draggable:
function ini_events(ele) {
ele.each(function () {
let eventObject = {
title: $(this).contents()
.filter(function () {
return !!$.trim(this.innerHTML || this.data);
})
.first().text(),
descrizione: $.trim($(this).contents()[3].firstChild.textContent),
ore: $(this).contents('#ore').val(),
color: $(this).css("background-color"),
};
$(this).data('event', eventObject);
$(this).draggable({
zIndex: 1070,
revert: true,
revertDuration: 0
});
});
}
ini_events($('#external-events div.external-event'));
I found the viewRender function that allow me to get the begin and the end of the displayed week:
$('#calendar').fullCalendar({
...
viewRender: function (element) {
var start = moment(element.intervalStart).format();
var end = moment(element.intervalEnd).subtract(1, 'd').format();
},
});
How can I use the start and end variable into the index method in the controller's query when I first show the calendar and every time I change the week?

You wish to pass two javascripts variables in php. There is huge literature on this issue, see for example
Laravel blade pass Javascript variable in php
There are many possible solutions.
I prefer pass the values of the variables through an url to a new method and after through session to the index method.
1) routes/web.php
Route::get('yourproject/{start}/{end}/getstartandend','yourController#getstartandend');
2) view
$('#calendar').fullCalendar({
...
viewRender: function (element) {
var start = moment(element.intervalStart).format();
var end = moment(element.intervalEnd).subtract(1, 'd').format();
},
});
...
appBase="{!!URL::to('/')!!}";
var route = appBase + "/yourproject/"+start+"/"+end+"/getstartandend";
$.get(route);
3) yourController
public function index ()
{
$corsi = Corso::select('titolo', 'descrizione','ore_45esimi')->get();
return view('lezione.calendario_lezioni')->with(array('corsi' => $corsi));
$start = session('start');
$end = session('end');
....
}
public function getstartandend ($start, $end)
{
session(['start' => $start]);
session(['end' => $end]);
}

Related

Mapping a nested object as an observable from a complex JSON using the create callback

I've got a complex object in a JSON format. I'm using Knockout Mapping, customizing the create callback, and trying to make sure that every object that should be an observable - would actually be mapped as such.
The following code is an example of what I've got:
It enables the user to add cartItems, save them (as a JSON), empty the cart, and then load the saved items.
The loading part fails: It doesn't display the loaded option (i.e., the loaded cartItemName). I guess it's related to some mismatch between the objects in the options list and the object bounded as the cartItemName (see this post), but I can't figure it out.
Code (fiddle):
var cartItemsAsJson = "";
var handlerVM = function () {
var self = this;
self.cartItems = ko.observableArray([]);
self.availableProducts = ko.observableArray([]);
self.language = ko.observable();
self.init = function () {
self.initProducts();
self.language("english");
}
self.initProducts = function () {
self.availableProducts.push(
new productVM("Shelf", ['White', 'Brown']),
new productVM("Door", ['Green', 'Blue', 'Pink']),
new productVM("Window", ['Red', 'Orange'])
);
}
self.getProducts = function () {
return self.availableProducts;
}
self.getProductName = function (product) {
if (product) {
return self.language() == "english" ?
product.productName().english : product.productName().french;
}
}
self.getProductValue = function (selectedProduct) {
// if not caption
if (selectedProduct) {
var matched = ko.utils.arrayFirst(self.availableProducts(), function (product) {
return product.productName().english == selectedProduct.productName().english;
});
return matched;
}
}
self.getProductColours = function (selectedProduct) {
selectedProduct = selectedProduct();
if (selectedProduct) {
return selectedProduct.availableColours();
}
}
self.addCartItem = function () {
self.cartItems.push(new cartItemVM());
}
self.emptyCart = function () {
self.cartItems([]);
}
self.saveCart = function () {
cartItemsAsJson = ko.toJSON(self.cartItems);
console.log(cartItemsAsJson);
}
self.loadCart = function () {
var loadedCartItems = ko.mapping.fromJSON(cartItemsAsJson, {
create: function(options) {
return new cartItemVM(options.data);
}
});
self.cartItems(loadedCartItems());
}
}
var productVM = function (name, availableColours, data) {
var self = this;
self.productName = ko.observable({ english: name, french: name + "eux" });
self.availableColours = ko.observableArray(availableColours);
}
var cartItemVM = function (data) {
var self = this;
self.cartItemName = data ?
ko.observable(ko.mapping.fromJS(data.cartItemName)) :
ko.observable();
self.cartItemColour = data ?
ko.observable(data.cartItemColour) :
ko.observable();
}
var handler = new handlerVM();
handler.init();
ko.applyBindings(handler);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://rawgit.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.js
"></script>
<div>
<div data-bind="foreach: cartItems">
<div>
<select data-bind="options: $parent.getProducts(),
optionsText: function (item) { return $parent.getProductName(item); },
optionsValue: function (item) { return $parent.getProductValue(item); },
optionsCaption: 'Choose a product',
value: cartItemName"
>
</select>
</div>
<div>
<select data-bind="options: $parent.getProductColours(cartItemName),
optionsText: $data,
optionsCaption: 'Choose a colour',
value: cartItemColour,
visible: cartItemName() != undefined"
>
</select>
</div>
</div>
<div>
<button data-bind="text: 'add cart item', click: addCartItem" />
<button data-bind="text: 'empty cart', click: emptyCart" />
<button data-bind="text: 'save cart', click: saveCart" />
<button data-bind="text: 'load cart', click: loadCart" />
</div>
</div>
What needs to be changed to fix it?
P.S.: I've got another piece of code (see it here) that demonstrates a persistance of the selected value even after changing the options - though there optionsValue is a simple string, while here it's an object.
EDIT:
I figured out the problem: the call ko.mapping.fromJS(data.cartItemName) creates a new productVM object, which is not one of the objects inside availableProducts array. As a result, none of the options corresponds to the productVM contained in the loaded cartItemName, so Knockout thereby clears the value altogether and passes undefined.
But the question remains: how can this be fixed?
In the transition from ViewModel -> plain object -> ViewModel you loose the relation between the products in your cart and the ones in your handlerVM.
A common solution is to, when loading a plain object, manually search for the existing viewmodels and reference those instead. I.e.:
We create a new cartItemVM from the plain object
Inside its cartItemName, there's an object that does not exist in handlerVM.
We look in handlerVM for a product that resembles this object, and replace the object by the one we find.
In code, inside loadCart, before setting the new viewmodels:
loadedCartItems().forEach(
ci => {
// Find out which product we have:
const newProduct = ci.cartItemName().productName;
const linkedProduct = self.availableProducts()
.find(p => p.productName().english === newProduct.english());
// Replace the newProduct by the one that is in `handlerVM`
ci.cartItemName(linkedProduct)
}
)
Fiddle: https://jsfiddle.net/7z6010jz/
As you can see, the equality comparison is kind of ugly. We look for the english product name and use it to determine the match. You can also see there's a difference in what is observable and what isn't.
My advice would be to use unique id properties for your product, and start using those. You can create a simpler optionsValue binding and matching new and old values happens automatically. If you like, I can show you an example of this refactor as well. Let me know if that'd help.

Why won't JS / JQuery read the text box values?

I have looked all over for a reason behind why this code does not work and I am stumped.
I have an ASPX page with C# code behind. The HTML mark-up has a JQuery dialog that functions properly. When the submit button is clicked the dialog closes and the data is passed to a web exposed method and is written to the database. All values are saved for the ddl and chkbox controls but the string value of the text box is empty. The database is set to NOT NULL for the field the text box is populating and the data is being saved so I know data is being passed but it is not the value entered into the text box.
The text box ID is txtCategoryName and the Client ID mode is set to static. I have tried to get the values using the following:
var CategoryName = $('#txtCategoryName').val();
var CategoryName = $('#txtCategoryName').text();
var CategoryName = $(document.getElementById('txtCategoryName')).text();
var CategoryName = $(document.getElementById('txtCategoryName')).val();
var CategoryName = document.getElementById('txtCategoryName').value;
All of these return the same blank field. I tried them one at a time.
Currently I am using this JS Code:
$(document).ready(function () {
var CategoryDialog = $(".EditCategories");
var BtnNew = $("#btnNew");
var CatDDL = document.getElementById("ddlCategoryParent3");
var CatChk = $("#chkCatActive").val();
var CategoryID = 0;
var CategoryName = $("#txtCategoryName").val();
var ParentID = CatDDL.options[CatDDL.selectedIndex].value;
if (CatChk) { CatChk = 1; } else { CatChk = 0; }
var CatDialog = $(CategoryDialog.dialog({
maxHeight: 1000,
closeOnEscape: true,
scrollable: false,
width: 650,
title: 'Category Editor',
autoOpen: false,
buttons: [
{
width: 170,
text: "Save",
icons: {
primary: "ui-icon-disk"
},
click: function () {
$(this).dialog("close");
window.alert(PageMethods.saveCat(CategoryName, ParentID, CategoryID, CatChk));
}
},
{
width: 170,
text: "Delete",
icons: {
primary: "ui-icon-circle-minus"
},
click: function () {
$(this).dialog("close");
}
},
{
width: 170,
text: "Cancel",
icons: {
primary: "ui-icon-circle-close"
},
click: function () {
$(this).dialog("close");
}
}
]
})
);
BtnNew.click(function () {
$(CatDialog).dialog('open');
$(CatDialog).parent().appendTo($("form:first"));
});
});
The code markup for the aspx page (categories.aspx)
<div class="EditCategories">
<div class="Table">
<div class="TableRow">
<div class="TableCell">
<div class="TextBlock220">Category Name </div>
</div><!-- End Table Cell -->
<div class="TableCell">
<input id="txtCategoryName" class="ControlTextBox" />
<!--<asp:TextBox ID="txtCategoryName" CssClass="ControlTextBox" runat="server" ClientIDMode="Static"></asp:TextBox>-->
</div><!--End Table Cell-->
</div><!-- End Row 1 -->
<div class="TableRow">
<div class="TableCell">
Parent Category
</div><!-- End Table Cell -->
<div class="TableCell">
<asp:DropDownList ID="ddlCategoryParent3" runat="server" CssClass="ControlDropDownList" ClientIDMode="Static"></asp:DropDownList>
</div><!--End Table Cell-->
</div>
<div class="TableRow">
<div class="TableCell">
Active
</div><!-- End Table Cell -->
<div class="TableCell">
<asp:Checkbox ID="chkCatActive" CssClass="ControlCheckBox" runat="server" ClientIDMode="Static"></asp:Checkbox>
</div><!--End Table Cell-->
</div><!-- End Row 3-->
</div>
</div>
The C# Code behind method for the ASPX page:
[System.Web.Services.WebMethod()]
[System.Web.Script.Services.ScriptMethod()]
public static string saveCat(string _Name_, int _parent_id_, int ID, int _Status_)
{
Category eCT = new Category();
eCT.CategoryName = _Name_;
eCT.ParentID = _parent_id_;
eCT.ID = ID;
eCT.Status = _Status_;
eCT.Save();
return eCT.resultMessage;
}
And the save method:
/// <summary>
/// If the ID = 0 the data is written as a new category.
/// If the ID is greater than 0 the data is updated.
/// </summary>
/// <returns>The objects result value will hold the result of the attempt to update data as type Boolean. The objects resultMessage value will contain the string result of the attempt to add data.</returns>
public void Save()
{
result = dl.CategoryExists(this);
if (result) { resultMessage = "The parent category already contains a category named " + CategoryName.Trim(); }
else {
if (ID > 0)
{
if (!result) { resultMessage = "There was an unexpected error updating " + CategoryName.Trim() + ". No changes were saved."; }
}
else
{
result = dl.InsertCategory(this);
if (!result) { resultMessage = "There was an unexpected error creating the Category."; }
}
}
if (result) { resultMessage = "New Category Successfully Created"; }
}
Any help is greatly appreciated thanks.
The issue here is you're attempting to get the value right as soon as the page loads, before the input field gets filled out. Place this code inside the button click function:
var CategoryName = document.getElementById('txtCategoryName').value;
and it should work for you. If not, let us know.
Your code should look something like this:
click: function () {
// note: CategoryID not used yet.
var CategoryName = $("#txtCategoryName").val();
var CatChk = $("#chkCatActive").val();
var CatDDL = document.getElementById("ddlCategoryParent3")
var ParentID = CatDDL.options[CatDDL.selectedIndex].value;
if (CatChk) { CatChk = 1; } else { CatChk = 0; }
$(this).dialog("close");
window.alert(PageMethods.saveCat(CategoryName, ParentID, CategoryID, CatChk));
}
You are fetching the values from your dialog at page startup time BEFORE they have been edited.
It looks like this:
var CategoryName = $("#txtCategoryName").val();
is run at page startup time before the page has been edited. This will fetch the default value for the input field and will never reflect any editing that is done on the page. The line of code above does not create a "live" connection with the input field on the page. It just gets the value at the time that line of code is run and from then on there is no connection to any edits made to the field.
I would think you want to fetch the value only later when you actually need to value for something. In general, you do not want to cache a value like this because the cached value gets out of sync with what is in the actual field on the page. Just fetch it at the very moment that you need it for something and it will never have a stale value.
If the place that you're using this value is inside the dialog click handler, then fetch it there so you are getting the latest value:
click: function () {
$(this).dialog("close");
var CatChk = $("#chkCatActive").val() ? 1 : 0;
var CategoryName = $("#txtCategoryName").val();
var CatDDL = document.getElementById("ddlCategoryParent3");
var ParentID = CatDDL.options[CatDDL.selectedIndex].value;
window.alert(PageMethods.saveCat(categoryName, ParentID, CategoryID, CatChk));
}

DropDownList Change() doesn't seem to fire

So, I have been bashing my head against the desk for a day now. I know this may be a simple question, but the answer is eluding me. Help?
I have a DropDownList on a modal that is built from a partial view. I need to handle the .Change() on the DropDownList, pass the selected text from the DropDownList to a method in the controller that will then give me data to use in a ListBox. Below are the code snippets that my research led me to.
all other controls on the modal function perfectly.
Can anyone see where I am going wrong or maybe point me in the right direction?
ProcessController
// I have tried with [HttpGet], [HttpPost], and no attribute
public ActionResult RegionFilter(string regionName)
{
// Breakpoint here is never hit
var data = new List<object>();
var result = new JsonResult();
var vm = new PropertyModel();
vm.getProperties();
var propFilter = (from p in vm.Properties
where p.Region == regionName && p.Class == "Comparable"
select p).ToList();
var listItems = propFilter.ToDictionary(prop => prop.Id, prop => prop.Name);
data.Add(listItems);
result.Data = data;
return result;
}
Razor View
#section scripts{
#Scripts.Render("~/Scripts/ui_PropertyList.js")
}
...
<div id="wrapper1">
#using (Html.BeginForm())
{
...
<div id="fancyboxproperties" class="content">
#Html.Partial("PropertyList", Model)
</div>
...
<input type="submit" name="bt_Submit" value="#ViewBag.Title" class="button" />
}
</div>
Razor (Partial View "PropertyList.cshtml")
...
#{ var regions = (from r in Model.Properties
select r.Region).Distinct(); }
<div>
<label>Region Filter: </label>
<select id="ddl_Region" name="ddl_Region">
#foreach (var region in regions)
{
<option value=#region>#region</option>
}
</select>
</div>
// ListBox that needs to update after region is selected
<div>
#Html.ListBoxFor(x => x.Properties, Model.Properties.Where(p => p.Class == "Comparable")
.Select(p => new SelectListItem { Text = p.Name, Value = p.Id }),
new { Multiple = "multiple", Id = "lb_C" })
</div>
...
JavaScript (ui_PropertyList.js)
$(function () {
// other events that work perfectly
...
$("#ddl_Region").change(function () {
$.getJSON("/Process/RegionFilter/" + $("#ddl_Region > option:selected").attr("text"), updateProperties(data));
});
});
function updateProperties(data, status) {
$("#lb_C").html("");
for (var d in data) {
var addOption = new Option(data[d].Value, data[d].Name);
addOption.appendTo("#lb_C");
}
}
The callback function passed to your $.getJSON method is wrong. You need to pass a reference to the function, not to invoke it.
Try this:
$.getJSON("/Process/RegionFilter/" + $("#ddl_Region > option:selected").text(), updateProperties);
Also, in order to get the text of the selected drop-down option, you need to use the text() function:
$("#ddl_Region > option:selected").text()
See Documentation

Knockout.js - Data binding outputting function text when not using parens

I am new to Knockout and have been trying to follow code examples and the documentation, but keep running into an issue. My data bindings printing the Knockout observable function, not the actual values held by my observable fields. I can get the value if I evaluate the field using (), but if you do this you do not get any live data-binding / updates.
Below are some code snippets from my project that are directly related to the issue I am describing:
HTML
<div class="col-xs-6">
<div data-bind="foreach: leftColSocialAPIs">
<div class="social-metric">
<img data-bind="attr: { src: iconPath }" />
<strong data-bind="text: name"></strong>:
<span data-bind="text: totalCount"></span>
</div>
</div>
</div>
Note: leftColSocialAPIs contains an array of SocialAPIs. I can show that code too if needed.
Initializing the totalcount attribute
var SocialAPI = (function (_super) {
__extends(SocialAPI, _super);
function SocialAPI(json) {
_super.call(this, json);
this.totalCount = ko.observable(0);
this.templateName = "social-template";
}
SocialAPI.prototype.querySuccess = function () {
this.isLoaded(true);
appManager.increaseBadgeCount(this.totalCount());
ga('send', 'event', 'API Load', 'API Load - ' + this.name, appManager.getRedactedURL());
};
SocialAPI.prototype.toJSON = function () {
var self = this;
return {
name: self.name,
isActive: self.isActive(),
type: "social"
};
};
return SocialAPI;
})(API);
Updating totalcount attribute for LinkedIn
var LinkedIn = (function (_super) {
__extends(LinkedIn, _super);
function LinkedIn(json) {
json.name = "LinkedIn";
json.iconPath = "/images/icons/linkedin-16x16.png";
_super.call(this, json);
}
LinkedIn.prototype.queryData = function () {
this.isLoaded(false);
this.totalCount(0);
$.get("http://www.linkedin.com/countserv/count/share", { "url": appManager.getURL(), "format": "json" }, this.queryCallback.bind(this), "json").fail(this.queryFail.bind(this));
};
LinkedIn.prototype.queryCallback = function (results) {
if (results != undefined) {
results.count = parseInt(results.count);
this.totalCount(isNaN(results.count) ? 0 : results.count);
}
this.querySuccess();
};
return LinkedIn;
})(SocialAPI);
In the <span data-bind="text: totalCount"></span>, I expect to see a number ranging from 0-Integer.MAX. Instead I see the following:
As you can see, its outputting the knockout function itself, not the value of the function. Every code example I've seen, including those in the official documentation, says that I should be seeing the value, not the function. What am I doing wrong here? I can provide the full application code if needed.
Not sure, but KO view models obviously tend to bind own (not inherited through prototypes) observable properties only. So you should rewrite your code to supply totalCount observable for every social network separately.

Reference Global jquery function variable within foreach loop

I want to change the value of a global jquery variable within the foreach loop every time there is a new model item. I want to add new dates to the calendar but cant do that until I can access these functions from foreach loop.
*edit***
I over simplified my example for the question which was answered correctly by vov v. The actual code will do a lot more than add a value as it will add data to a calendar. I've added more code to show what it will do a little better
jquery:
<div id="calendar" style="width: 500px;" />
<script type="text/javascript">
$(document).ready(function () {
var calendar = $('#calendar').glDatePicker(
{
showAlways: true,
borderSize: 0,
dowOffset: 1,
selectableDOW: [1, 2, 3, 4, 5],
selectableYears: [2012, 2013, 2014, 2015],
specialDates: [
{
date: new Date(2013, 0, 8),
data: { message: 'Meeting every day 8 of the month' },
repeatMonth: true
},
{
date: new Date(2013, 5, 7),
data: { message: 'Meeting every day 8 of the month' }
},
],
onClick: function (target, cell, date, data) {
target.val(date.getFullYear() + ' - ' +
date.getMonth() + ' - ' +
date.getDate());
if (data != null) {
alert(data.message + '\n' + date);
}
}
}).glDatePicker(true);
$('#visible').change(function () {
var showAlways = eval($(this).val());
calendar.options.showAlways = showAlways;
if (!showAlways) {
calendar.hide();
}
else {
calendar.show();
}
});
});
var value = 0;
$('#total').click(function () {
alert(value);
});
function add() {
// will eventually add new specialDates to the calendar taken from model items
//test lines
//value = value + 1;
//return value;
}
</script>
razor view:
<input type="button" id="total" />
#foreach (var item in Model){
if (item.AppointmentStatus == "active")
{
// item display code
#: <script type="text/javascript"> add();</script>
}
if (item.AppointmentStatus == "history")
{
// item display code
}
}
I run this and get error below as it doesnt see the other code
'0x800a1391 - JavaScript runtime error: 'add' is undefined'
If you just want to capture "a count" that you want to send down to your client then you can simply do it like this:
<script>
var value = '#Model.Count';
// the rest of you script goes here
$(document).ready(function () {
$('#total').click(function () {
alert(value);
}
});
</script>
So say you have 7 items in your model, then the html that will be generated is this:
var value = 7;
and when you click on that total element it will give you an alert with the text 7.
Try moving your add function and variable declaration outside of doc ready. The functions in doc ready are not available until your razor has executed.

Categories