Backbonejs.org says
"— while very deliberately avoiding painting you into a corner by making any decisions that you're better equipped to make yourself."
So I have a BackBone view like this:
var LotsaToys= Backbone.View.extend({
el: '#CaveToy',
events: {
'click .slide': 'slideAnimal'
},
initialize: function (obj) {
this.collection = new Yoyo(obj);
this.render();
},
render: function () {
this.collection.each(function (item) {
this.renderToy(item);
}, this);
},
renderBook: function (item) {
var ToyView = new oneToy({
model: item
});
this.$el.append(ToyView .render().el);
}
});
And simple Jquery code like this:
$(document).ready(function () {
var sudoSlider = $("#slider").sudoSlider({
vertical: true,
continuous: false
});
});
Do I combine these to by simply adding the JQuery code in the slide function?
(Simple Jquery image slider)
slideAnimal: function (event) {
$(document).ready(function () {
var sudoSlider = $("#slider").sudoSlider({
vertical: true,
continuous: false
});
});
});
Im a noob, so please give me a explanation ,not sure what way is 'good practice' or what way would be better to implement..
You need place it after render
render: function () {
this.collection.each(function (item) {
this.renderToy(item);
}, this);
this.slideAnimal();
},
slideAnimal: function() {
this.$("#slider").sudoSlider({
vertical: true,
continuous: false
});
}
Related
I'm trying to open a Kendo UI kendoWindow from within an MVC View. I also use a Partial View as the content of the kendoWindow. Moreover, I use the Kendo UI MVVM pattern to bind my elements.
First let me to show you my main View and my pop-up Partial View (kendoWindow).
The important part of my main View (Parent) is as follows:
#{
ViewBag.Title = "My Main View";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/ViewModel/main.js"></script>
<script src="~/Scripts/InitView/main.js"></script>
<script type="text/javascript">
var viewModel;
$(function () {
viewModel = initVm({
GetPartialContent_Url: '#Url.Action("GetPartialContent")'
});
initView(viewModel);
kendo.bind($("#container"), viewModel);
viewModel.Onread();
});
</script>
<div id="container">
<div id="Window-box"></div>
// Some other elements like the button which opens the kendoWindow are defined here.
</div>
My initVm is as follows:
function initVm(arg) {
var vm = kendo.observable({
onOpenKendoWindow: function () {
$("#Window-box").kendoWindow({
iframe: true,
content: arg.GetPartialContent_Url,
title: 'Some Title',
width: 500,
height: 'auto',
close: function (e) {
//Is it possible to get some data from kendoWindow (Partial View) here?
}
});
var dialog = $("#Window-box").data("kendoWindow");
dialog.maximize();
}
});
return vm;
}
Until now, I showed you the important parts of my main View. Now I want to show you the important parts of my kendoWindow (Partial View).
My Partial View which is used as the content of the kendoWindow is as follows:
#{
Layout = "~/Views/Shared/_PartialLayout.cshtml";
}
<script src="~/Scripts/ViewModel/partial.js"></script>
<script src="~/Scripts/InitView/partial.js"></script>
<script type="text/javascript">
var partialVM;
$(function () {
partialVM = initPartialVm({
GetTransactions_Url: '#Url.Action("GetTransactions", "Account")'
});
initPartialView(partialVM);
kendo.bind($("#container"), partialVM);
});
</script>
<div id="container">
<div id="gridTransactions"></div>
</div>
And my initPartialVm is as follows:
function initPartialVm(arg) {
var vm = kendo.observable({
onSelectTransaction: function () {
// KendoWindow should be closed here and passing some data from here to main View (close event of kendowWindow);
}
});
return vm;
}
Note: The 'gridTransactions' is a Kendo UI GridView (inside of kendoWindow - Partial View). Each rows of this grid has a select button and the 'onSelectTransaction' function is fired when each of these select buttons is clicked.
And finally, the main question is that, how can I close the kendowWindow by clicking each select button of the GridView and pass some data to the close event of the kendowWindow?
Yes it is possible. I found it much easier and a bit cleaner to wrap all the dialog functionality up into a dialog controller and extend it a bit in javascript.
Once the .js part is done it makes for a cleaner use. If you don't prefer to do this then look for the findDialog function below (it shows how get a handle to a dialog and call the close method on it).
As far as sending data on close, It would be easy to add a callback in the dialog to be called when the dialog is closed, supplied on invocation, then add a property in the widget to set the custom data to pass through in the dialogs close() back to the consumers event handler.
Also, please note I am no javascript expert, it took me longer than I would like to admit to work the bugs out of this but it has held up solidly for about 6 years. Feel free to offer suggestions.
In Bundle Config:
bundles.Add(new ScriptBundle("~/bundles/myCustom").Include(
...
"~/Scripts/MyCustom/MyCustomDialogs.js",
...
));
Where you register scripts:
#Scripts.Render("~/bundles/MyCustom")
In your index view or parent view :
<div id="_applicationDialogs"></div>
<div id="_mainAppContentLoadsHere"></div>
var _mainDialogController;
$(document).ready(function () {
...
_mainDialogController = $("#_applicationDialogs").kendoMyCustomDialogController().data("kendoMyCustomDialogController");
...
}
Where you want to invoke the dialog: SomePartial
function lnkDetailsOnClick(someID) {
_mainDialogController.createDialog({
dialogId: "frmUserDetail_" + someID,
modal: false,
title:"Daily Details",
pin: true,
height: 575,
width: 1025,
actions: ["Refresh", "Maximize", "Minimize", "Pin", "Close"],
url: '#Url.Action("SomePartialView", "SomeController")',
data:{
someID: someID,
dialogName:'frmUserDetail_'+ someID //NOTE : This will come back in the invoked partial as Model.DialogName so it can be dismissed with ease.
}
});
}
Dismissing the Dialog Inside of SomePartial :
#model MyModelThatHasTheDialogHandle
function btnClose_Click() {
var dialog = _mainDialogController.findDialog('#Model.DialogName');
dialog.close();
}
Now for the long .js file :
(function ($) {
var kendo = window.kendo,
ui = kendo.ui,
Widget = ui.Widget;
var MyCustomDialogController = Widget.extend({
init: function (element, options) {
var that = this;
Widget.fn.init.call(this, element, options);
that._create();
},
onResize: function () { },
options: {
modal: true,
dialogId: "dlgController1",
url: "",
data: null,
pin: false,
width: 300,
height: 300,
actions:["Close"],
title: "Information",
disableMaximize:false,
name: "MyCustomDialogController",
autosize: false,
onDialogClosed: null,
hideOnClose: false
},
_create: function () {
var that = this;
},
createDialog: function (options) {
var that = this;
var wrapperName = options.dialogId + "_wrapper";
that.element.append("<div id='" + wrapperName + "'></div>");
var wrapperElement = that.element.find("#" + wrapperName);
wrapperElement.kendo_MyCustomDialog(options);
},
findDialog: function (dialogId) {
that = this;
var wrapperName = dialogId+"_wrapper";
var dialog = $("#" + wrapperName);
//var dialog = wrapper.find("#" + dialogId);
return dialog.data("kendo_MyCustomDialog");
},
forceCloseAllDialogs: function ()
{
that = this;
$('.MyCustom-window').each(function () {
$(this).data("kendoWindow").close();
});
},
isModalWindowActive: function ()
{
that = this;
return $('.MyCustom-window-modal').length > 0;
},
currentModalWindow: function () {
that = this;
return that.findDialog($('.MyCustom-window-modal')[0].id);
}
});
ui.plugin(MyCustomDialogController);
})(jQuery);
(function ($) {
var kendo = window.kendo,
ui = kendo.ui,
Widget = ui.Widget;
var _MyCustomDialog = Widget.extend({
init: function (element, options) {
var that = this;
Widget.fn.init.call(this, element, options);
that._create();
},
onResize: function () { },
options: {
modal: true,
dialogId: "frmMain",
url: "",
data: null,
pin: false,
width: 300,
height: 300,
actions: ["Close"],
title: "Information",
name: "_MyCustomDialog",
disableMaximize:false,
autosize: false,
onDialogClosed: null,
hideOnClose:false
},
_create: function () {
var that = this;
that.isModalWindowActive = true;
that.modifiedData = false;
that.frmElement = $("#" + that.options.dialogId).data("kendoWindow");
if (that.frmElement == null) {
var template ;
if(that.options.modal)
template = kendo.template(that._templates.divModalFormWrapper);
else
template = kendo.template(that._templates.divFormWrapper);
that.wrapper = $(template(that.options));
that.element.append(that.wrapper);
if (that.options.autosize)
{
that.options.height =null;
that.options.width = null;
}
that.frmElement = that.wrapper.kendoWindow({
title: "Loading...",
modal: that.options.modal,
visible: that.options.autosize,
draggable: true,
resizeable:!that.options.disableMaximize,
width: that.options.width,
height: that.options.height,
resizeable: true,
pinned:that.options.pin,
resize: function () {
that.onResize();
},
content: {
url: that.options.url,
data: that.options.data,
type: "POST",
datatype: "json",
traditional: true
},
refresh: function () {
that.frmElement.title(that.options.title);
if (that.options.autosize) {
that.frmElement.center();
}
},
actions: that.options.actions,
close: function (e) {
that.IsModalWindowActive = false;
if (that.options.hideOnClose == false) {
if (that.frmElement != null)
that.frmElement.destroy();
this.destroy();
that.wrapper.remove("#" + that.options.dialogId);
that.wrapper.empty();
}
if (that.options.onDialogClosed) {
that.options.onDialogClosed(that.modifiedData);
}
}
}).data("kendoWindow");
}
if (that.options.autosize)
that.frmElement.center().open();
else if (that.options.hideOnClose == true)
that.frmElement.open();
else
that.frmElement.center().open();
if (that.options.pin)
that.frmElement.pin();
},
setModifiedFlag:function(modified)
{
var that = this;
that.modifiedData = modified;
},
close: function () {
var that = this;
that.frmElement.close();
},
show: function () {
var that = this;
that.wrapper.show();
that.frmElement.open();
},
setTitle: function (title) {
var that = this;
that.frmElement.title(title);
},
height: function () {
var that = this;
var wtfHeight = that.frmElement.options.height;
if (isNaN(wtfHeight)) {
if (wtfHeight.indexOf("px") >= 0)
wtfHeight = wtfHeight.replace("px", "");
}
return wtfHeight;
},
_templates: {
divModalFormWrapper: "<div id='#=dialogId#' class='MyCustom-window MyCustom-window-modal'></div>",
divFormWrapper: "<div id='#=dialogId#' class='MyCustom-window'></div>"
}
});
// add the widget to the ui namespace so it's available
ui.plugin(_MyCustomDialog);
})(jQuery);
i want modification the base_calendar.js with new custom function like below
CalendarNotification = require('base_calendar.base_calendar');
console.log("Masuk sini bawah");
CalendarNotification.include({
'click .link2showed': function() {
console.log("ndak yo mlebu kene to");
var action = {
type: 'ir.actions.act_window',
res_model: 'crm.lead',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
res_id: 16644
};
this.do_action(action);
},
});
and this a base_calendar.js odoo addons
var Notification = require('web.notification').Notification;
var CalendarNotification = Notification.extend({
template: "CalendarNotification",
init: function(parent, title, text, eid) {
this._super(parent, title, text, true);
this.eid = eid;
this.events = _.extend(this.events || {}, {
'click .link2event': function() {
var self = this;
this.rpc("/web/action/load", {
action_id: "calendar.action_calendar_event_notify",
}).then(function(r) {
r.res_id = self.eid;
return self.do_action(r);
});
},
'click .link2recall': function() {
this.destroy(true);
},
'click .link2showed2': function() {
this.destroy(true);
this.rpc("/calendar/notify_ack");
},
});
},
});
How do I fix that and what causes it? I've been several times custom function JS like that and it worked well.
Thank in advance for any pointers.
I have two items that should be visible only when a given observable evaluates to false, and one item that should be visible when the same item evaluates to true. It works fine if I only have two items whose visibility depends on the observable. However, when a third item is introduced, it stops working.
ViewModel:
var viewModel = {
editable: ko.observable(false),
edit: function () {
this.editable(true);
},
delete: function () {
this.editable(true);
},
cancel: function () {
this.editable(false);
},
save: function () {
this.editable(false);
}
};
ko.applyBindings(viewModel);
A fiddle is available here: http://jsfiddle.net/mupersan82/Y6eKS/10/
The fiddle is working. Uncomment one of the "button" items to see the problem.
Can anyone identify what the problem is?
Regards, Anders
delete is a Javascript operator and therefore a reserved word (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete). Renaming the function fixes it:
var viewModel = {
editable: ko.observable(false),
edit: function () {
this.editable(true);
},
remove: function () {
this.editable(true);
},
cancel: function () {
this.editable(false);
},
save: function () {
this.editable(false);
}
};
ko.applyBindings(viewModel);
(fiddle: http://jsfiddle.net/Y6eKS/11/)
Imagine the following simple model and view:
app.Messages = Backbone.Model.extend({
defaults: function() {
return {
message: "an empty message…",
messageActive: false
};
}
});
app.MessageView = Backbone.View.extend({
tagName: "li",
template: _.template($("#template").html()),
events: {
"click": "open"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "message:hide", this.hide);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
open: function() {},
hide: function() {
this.model.set("messageActive", false);
this.$el.removeClass("show-message");
}
});
The model is part of a collection and everything is held together by a parent view (simplified for this question):
app.AppView = Backbone.View.extend({
el: "#messages",
initialize: function() {
this.listenTo(app.messages, "message:show", this.foo);
},
foo: function(model) {
var open = app.messages.where({messageActive: true});
open.forEach(function(e, i) {
if (model != e) {
e.trigger("message:hide");
}
});
},
Only one message of the collection can be active at any given time. This means that as soon as a closed message is clicked, any open message will be closed. The question now concerns the functionality of app.MessageView.open(). It could imho either use jQuery.siblings() to hide the message of all its siblings and show its own:
open: function() {
this.$el
.addClass("show-message")
.siblings(".show-message")
.removeClass("show-message");
}
Personally I think this solution quite elegant, I only dislike that it kind of needs to know that it has siblings. My other implementation would use a custom event that gets intercepted by the parent view (app.AppView.foo()) that then calls app.MessageView.close() on every open message:
open: function() {
this.model.set("messageActive", true).trigger("message:show", this.model);
this.$el.addClass("show-message");
}
This feels a little more "backbone" to me, but seems a little over-engineered. ;-)
I could event think of a third possibilty where the parent view simply keeps track of the last opened model itself and triggers any close event that is intercepted by the models view (seems manageable but not very backbone-like to me ;-) ). I've already covered quite some blog posts and questions here on SO, but came to no real satisfactory conclusion. Maybe someone can shed some light this specific question.
EDIT: Completely missed the click event on the messageview so revamped my answer.
You could trigger a click event on the model whenever the view is clicked, then wait for the parentview to trigger show and hide events on the model.
app.MessageView = Backbone.View.extend({
tagName: "li",
template: _.template($("#template").html()),
events: {
"click": "isClicked"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
this.listenTo(this.model, "message:show", this.open);
this.listenTo(this.model, "message:hide", this.hide);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
isClicked: function(){
this.model.trigger("message:clicked");
},
open: function() {
this.model.set("messageActive", true);
this.$el.addClass("show-message");
},
hide: function() {
this.model.set("messageActive", false);
this.$el.removeClass("show-message");
}
});
Your parentview is now responsible of sending the right events to the models whenever one of it's childviews is clicked.
app.AppView = Backbone.View.extend({
el: "#messages",
initialize: function() {
this.listenTo(app.messages, "message:clicked", this.foo);
},
foo: function(model) {
var open = app.messages.where({messageActive: true});
open.forEach(function(e, i) {
if (model != e) {
e.trigger("message:hide");
}
});
// Because we retrieved the currently active models before sending this event,
// there will be no racing condition:
model.trigger("message:show");
},
Perhaps not the shortest way to achieve this sorta thing. But it is completely event driven and customizable.
As a heads-up, I ended up with a slight variation of the accepted answer. Instead of triggering custom events, the views now listen to the change of the models messageActive attribute and act accordingly.
app.MessageView = Backbone.View.extend({
tagName: "li",
template: _.template($("#template").html()),
events: {
"click": "isClicked"
},
initialize: function() {
this.listenTo(this.model, "change:messageActive", this.showMessage);
},
render: function() {
this.$el.html(this.template(this.model.toJSON())).addClass("sprite");
return this;
},
isClicked: function() {
this.model.set("messageActive", true);
},
showMessage: function(model, value) {
this.$el.toggleClass("show-message", value);
}
});
app.AppView = Backbone.View.extend({
el: "#messages",
initialize: function() {
this.listenTo(app.messages, "change:messageActive", this.hideMessages);
},
hideMessages: function(model, value) {
if (value) {
var open = app.messages.where({messageActive: true});
open.forEach(function(e, i) {
if (model != e) {
e.set("messageActive", false);
}
});
}
},
In my backbone+marionette application I have used morris.js Line Chart.
This chart takes an array of data from the model. And must be created after DOM creating (DOM dependent).
Сode to create the chart:
_createChartData: function () {
var trends = this.model.get("trends"),
chartData = [];
$.each(trends, function (x, y) {
// some actions with data
});
return chartData;
},
_createChart: function () {
Morris.Line({
element: 'promo-chart',
data: this._createChartData(),
xkey: 'date',
ykeys: ['y', 'g'],
});
}
View and Controller:
define(['utils/hbs', 'modules/model/promo-mdl'], function( Hbs, Promo){
var PromoView = Marionette.ItemView.extend({
initialize: function () {
this._events();
},
template: Hbs.tf('modules/promo'),
templateHelpers: {
// handlebars helpers
},
events: {
'click #submitBtn' : '_savePromo',
},
_events: function () {
this.listenTo(this.model, 'change', this.render);
},
_savePromo: function () {
// save data
}
});
return Marionette.Controller.extend({
initialize: function (config) {
this.region = config.region;
this.model = new Promo({});
this.model.fetch();
this._events();
},
_events: function () {
App.vent.on('show:promo', this._show, this);
},
_show: function () {
this.view = new PromoView({ model: this.model });
this.region.show(this.view);
}
});
});
I tried to create this chart in View, but got an errors - empty data or no chart element in DOM.
Where to create this chart, in View or Controller? And which event to use? initialize, onShow or onRender?
It seems like you're creating the chart when the element it uses isn't in the DOM yet. Try creating the chart in the view's onShow function:
var PromoView = Marionette.ItemView.extend({
// code...
onShow: function(){
// create chart here
}
});
onDomRefresh function in the view could be what you are looking for.