How do I access a variable from a different view?
I want to access selectedColor in another view:
'onClickColor': function(e) {
var view = this,
selectedColor = $(e.currentTarget).data('color'), //$(this).data('color');
style = $('<style>.highlight { color: ' + selectedColor +'; margin-left: 4px;}</style>');
$('.initial').css('color', selectedColor);
$('.highlight').css('color', selectedColor);
$('html > head').append(style);
//view.canvasColorPick(e);
},
And then in another view, I want to pass the variable selectedColor using ajax on a form submission.
'formCollect' : function (e) {
var view = this;
var fname = $('#your_name').val();
var email = $('#email').val();
var state = $('#state').val();
var color = selectedColor;
var url = 'services/users/add?name='+fname+'&email='+email+'&state='+state+'&color='+color+'';
$.get(url);
},
It should be the best way – to subscribe one view to events of another one. Since you did not provide the scope your views was instantiated I assume both of them are in the global scope. If you don't understand what scope mean please read this:
What is the scope of variables in JavaScript?
So assume your first view instantiated like this: firstView = new FirstView();. The second one is secondView = new SecondView();.
When the color changed in firstView trigger an event: this.trigger( "Color changed", {newColor: "black"} );. Please make sure that both views was instantiated before this event triggered.
Now you need to subscribe secondView on the event of firstView: secondView.listenTo( firstView, "Color changed", secondView.handleColorChanged );
Here handleColorChanged is the event handler which will get event params as arguments.
Now alltogeather:
var FirstView = Backbone.view.extend({
/** your casual code here */
'onClickColor': function(e) {
/** your code here */
this.trigger( "Color changed", { newColor: "black" });
}
});
var SecondView = Backbone.view.extend({
/** your casual code here */
'handleColorChanged': function( eventData ) {
console.log( eventData );
}
});
var firstView = new FirstView(),
secnodView = new SecondView();
secondView.listenTo( firstView, "Color changed", secondView.handleColorChanged );
This is not the only one way to solve your task. But this is the way to separate views from each other. No one view knows about another one what is mostly needed in big applications, makes easy debugging process and so on.
Related
I have a button:
var oButton = new sap.m.Button({
text: "Press me",
press: function (oEvent) {
if (! this._oPopover) {
this._oPopover = sap.ui.xmlfragment("sap.my.library.Popover", this);
this._oPopover.setModel(true);
}
var oButton = oEvent.getSource();
var oMyControl= this._oPopover.getAggregation('content')[0];
oMyControl.setPlaceholder("Hello World");
jQuery.sap.delayedCall(0, this, function () {
this._oPopover.openBy(oButton);
});
}
})
And i have my xml Fragment:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:d="sap.my.library">
<Popover>
<d:myControl value=""/>
<!--<Input></Input>-->
</Popover>
</core:FragmentDefinition>
Now, when I click the button, nothing happens, only if I click it many times, my control appears.
I can say that for every 10 quick buttons clicks, my control appears maybe once.
Any suggestions?
Thanks!
this._oPopover is defined within an anonymous function declared within the button declaration.
IT CANNOT refer to a view field as you probably expect it to.
My suggestion :
var onButtonPress = function (oEvent) {
if (! this._oPopover) {
this._oPopover = sap.ui.xmlfragment("sap.my.library.Popover", this);
this._oPopover.setModel(true);
}
var oButton = oEvent.getSource();
var oMyControl= this._oPopover.getAggregation('content')[0];
oMyControl.setPlaceholder("Hello World");
jQuery.sap.delayedCall(0, this, function () {
this._oPopover.openBy(oButton);
});
}
var oButton = new sap.m.Button({
text: "Press me",
press: this.onButtonPressed.bind(this)
});
[code not tested!]
Could it be that 'this' is not referring to what you expect because your object is created within an anonymous function? See this explanation (ahem)
I believe the preferred pattern is to have the button press call a function in the main view where 'this' refers to the view scope.
I have a working example as follows: in then main view I have a button...
<Button
press="showDialogue"
/>
and the code of the view controller...
showDialogue: function(){
if(!this._oConfirmDialog){
this._oConfirmDialog = sap.ui.xmlfragment("sapui5.muSample.view.ConfirmDialog", this);
this.getView().addDependent(this._oConfirmDialog);
}
this._oConfirmDialog.open();
},
Note the use of view.addDependent() which lets the dialogue fragment access the view model AND destroys the dialogue instance in line the main view lifecycle events (according to the docs).
Or...you could create a local variable to capture the 'this' you actually want to refer to...
var contextThis = this;
just before your line starting
var oButton = ...
then change references to this inside the anon function to contextThis if you can't change the pattern.
I want an Action event (on="") to trigger when there is a transition to a new Route.
I've seen the list of Action event handlers and closest I could find is attaching the action to the largest HTML element on the page and triggering it with 'Mousemove". This is a terribly flawed away of going about what I want to do.
So just to draw it out.
<div {{action 'displayEitherHtml1or2'}} class="largestDOMelement">
{{#if showHtml1}}
// html 1 inside
{{/if}}
{{#if showHtml2}}
// html 2 inside
{{/if}}
</div>
'/objects' is a list of objects and clicking one leads to 'object/somenumber'. The action should automatically trigger when I enter the 'object/somenumber' page.
UPDATE: I've taken the contents from the previous update and dumped them into my DocRoute, but nothing it being triggered when I transition to 'doc' through {{#link-to 'doc' this.docID}} {{docTitle}}{{/link-to}}
VpcYeoman.DocRoute = Ember.Route.extend(VpcYeoman.Authenticated,{
toggleLetterSwitch: false,
togglePermitSwitch: false,
activate: function () {
var docTemplateID = this.get('docTemplateID');
if ( docTemplateID == 2) {
this.set('toggleLetterSwitch', true);
this.set('togglePermitSwitch', false);
console.log('docTemplateID equals 2');
} else {
this.set('toggleLetterSwitch', false);
this.set('togglePermitSwitch', true);
}
}
});
UPDATE DOS: setDocID is set in the DocsController to 1. Here's the whole thing.
VpcYeoman.DocsController = Ember.ArrayController.extend({
tempDocId: 1,
actions: {
addDoc: function (params) {
var docTitle = this.get('docTitle');
var docTemplateID = 1;
var docTemplateID = this.get('tempDocId');
console.log(this.get('tempDocId'));
var store = this.store;
var current_object = this;
var doc = current_object.store.createRecord('doc', {
docTitle:docTitle,
docTemplateID:docTemplateID
});
doc.save();
return true;
},
setDocId: function (param) {
this.set('tempDocId', param);
console.log(this.get('tempDocId'))
},
}
});
As #fanta commented, it seems like you're looking for the activate hook within your Route. This gets called when you enter the route where you define it. If you want to call it on every transition, you might consider defining a base route for your application and extending that instead of Em.Route:
App.BaseRoute = Em.Route.extend(
activate: function () {
// Do your thing
}
);
App.YourRoutes = App.BaseRoute.extend()
It's possible that there's a more appropriate place/time to do this, but without knowing quite what your action does, this is probably the best guess.
ETA: Looking at your edit, you won't want all your routes to extend App.BaseRoute the way I did it above; you should probably just include that activate hook explicitly in the routes which need it.
I'm developing a web app using Backbonejs.
I have a use case where I have to pass the new position of div1 to a double click event handler of a Backbone view.
My code looks like
var MyView = Backbone.Views.extend({
events: {
'dblclick #div1' : 'div1ClickHandler' //here I want to pass new offset for #div1
}
});
div1ClickHandler: function()
{
......
}
var myView = new MyView({model: myModel,el : #div1});
You can do that: inside div you need to add a new field with name data-yourfieldName and from js call that:
yourFunctionName: function(e) {
e.preventDefault();
var email = $(e.currentTarget).data("yourfieldName");
}
Assuming that your view element is a child element of the jquery widget, the best thing is probably to grab the values you need in the click handler:
var MyView = Backbone.Views.extend({
events: {
'dblclick #div1' : 'div1ClickHandler'
}
});
div1ClickHandler: function()
{
var $this = $(this);
var $widget = $this.parents('.widget-selector:first');
$this.offset($widget.offset());
$this.height($widget.height());
$this.width($widget.width());
}
var myView = new MyView({model: myModel,el : #div1});
If the jquery widget is always the direct parent of your view element, you can replace parents('.widget-selector:first') with parent(); otherwise, you'll need to replace .widget-selector with a selector that will work for the jquery widget.
You can pass widget in view itself, then you will have full control over widget.
var MyView = Backbone.Views.extend({
initialize: function(options) {
this.widget = options.widget; // You will get widget here which you passed at the time of view creation
}
events: {
'dblclick #div1' : 'div1ClickHandler' //here I want to pass new offset for #div1
}
});
div1ClickHandler: function() {
// Query to fetch new position and dimensions using widget
// update the respective element
}
var myView = new MyView({model: myModel, el: $('#div1'), widget: widgetInstance});
I'm learning javascript and have a question about listening and dispatching events with jQuery.
In my Model, I have a function that triggers a change event:
Model.prototype.setCurrentID = function(currentID) {
this.currentID = currentID;
$('body').trigger('change');
}
The trigger event requires an element, so I bound it to the 'body'. Is this good practice or bad practice?
In AS3, which I'm more familiar, I would simply dispatch a global event from the model, passing in a const value, listening for this event with an instance of the Model:
var model:Model = new Model();
model.addEventListener(CONST_VALUE, handlerFunction);
In jQuery, within my View object, I need to attach an element to the listener as well, so I bound it to the 'body' once again:
var View = function(model, controller) {
var model;
var controller;
this.model = model;
this.controller = controller;
$('body').change(function(evt) { updateSomething(evt); });
function updateSomething(evt){console.log('updating...')};
}
It's working, but I'm interested in your take on the subject.
I recommend using a private dispatcher, something that isn't exposed to the public.
For instance, your logic may fail if the user or a plugin unbinds all the events on the body(your dispatcher) :
$('body').unbind();
This can be avoided by creating a dom node and not expose it to the end user (do not append it to the dom) :
var dispatcher = $('<div />');
Model.prototype.setCurrentID = function(currentID) {
this.currentID = currentID;
dispatcher.trigger('change');
}
var View = function(model, controller) {
this.model = model;
this.controller = controller;
dispatcher.bind('change',function(evt) { updateSomething(evt); });
function updateSomething(evt){console.log('updating...')}
}
Another good thing to have in mind when developing event-programming app with jQuery is that jQuery allows you to bind/trigger custom events and also allows you to namespace your events. This way you can control more efficiently the event binding and triggering :
Model.prototype.setCurrentID = function(currentID) {
this.currentID = currentID;
dispatcher.trigger('modelIdChange.' + this.currentID);
}
Model.prototype.destroy = function() {
// unbind all the event handlers for this particular model
dispatcher.unbind('.'+this.currentID);
}
var View = function(model, controller) {
/*...*/
// this will be triggered for all the changes
dispatcher.bind('modelIdChange',function(evt) { updateSomething(evt); });
// this will be triggered only for the model with the id "id1"
dispatcher.bind('modelIdChange.id1',function(evt) { updateSomething(evt); });
/*...*/
}
I'd go a step further and create custom global events. With jQuery you can trigger a global custom event like so:
$.event.trigger('change');
Any element can subscribe to that event:
$('#myDiv').bind('change', function() {
console.log($(this));
});
The this keyword in the event handler is the DOM element which subscribed to the triggered event.
My objections are:
I wouldn't bind events that have the same name as broswer events, there might be interferences.
Your code works if you have one model, but if you have 2 or more, you'd want to separate them, and not bind/trigger both on the same element.
How about:
Model.prototype.bind = function(event, func) {
if (!this._element) this._element = $('<div>');
this._element.bind(this.name+'_'+event, $.proxy(func, this));
return this;
};
Model.prototype.trigger = function(event) {
if (!this._element) this._element = $('<div>');
this._element.trigger(this.name+'_'+event);
return this;
};
This way you solve both. Note I'm appending this.name+'_' to event names (which assume each model has some sort of name, and makes sure events won't match with browser events), but you can also drop the the prefix.
I'm also using $.proxy in bind so that the this in the event handler refers to the model.
var View = function(model, controller) {
....
model.bind('change', function() {...});
}
The 2nd answer to this question nicely explains how event declarations in Backbone.js views are scoped to the view's el element.
It seems like a reasonable use case to want to bind an event to an element outside the scope of el, e.g. a button on a different part of the page.
What is the best way of achieving this?
there is not really a reason you would want to bind to an element outside the view,
there are other methods for that.
that element is most likely in it's own view, (if not, think about giving it a view!)
since it is in it's own view, why don't you just do the binding there, and in the callback Function,
use .trigger(); to trigger an event.
subscribe to that event in your current view, and fire the right code when the event is triggered.
take a look at this example in JSFiddle, http://jsfiddle.net/xsvUJ/2/
this is the code used:
var app = {views: {}};
app.user = Backbone.Model.extend({
defaults: { name: 'Sander' },
promptName: function(){
var newname = prompt("Please may i have your name?:");
this.set({name: newname});
}
});
app.views.user = Backbone.View.extend({
el: '#user',
initialize: function(){
_.bindAll(this, "render", "myEventCatcher", "updateName");
this.model.bind("myEvent", this.myEventCatcher);
this.model.bind("change:name", this.updateName);
this.el = $(this.el);
},
render: function () {
$('h1',this.el).html('Welcome,<span class="name"> </span>');
return this;
},
updateName: function() {
var newname = this.model.get('name');
console.log(this.el, newname);
$('span.name', this.el).text(newname);
},
myEventCatcher: function(e) {
// event is caught, now do something... lets ask the user for it's name and add it in the view...
var color = this.el.hasClass('eventHappened') ? 'black' : 'red';
alert('directly subscribed to a custom event ... changing background color to ' + color);
this.el.toggleClass('eventHappened');
}
});
app.views.sidebar = Backbone.View.extend({
el: '#sidebar',
events: {
"click #fireEvent" : "myClickHandler"
},
initialize: function(){
_.bindAll(this, "myClickHandler");
},
myClickHandler: function(e) {
window.user.trigger("myEvent");
window.user.promptName();
}
});
$(function(){
window.user = new app.user({name: "sander houttekier"});
var userView = new app.views.user({model: window.user}).render();
var sidebarView = new app.views.sidebar({});
});
Update: This answer is no longer valid/right. Please see other answers below!
Why do you want to do this?
Apart from that, you could always just bind it using regular jQuery handlers. E.g.
$("#outside-element").click(this.myViewFunction);
IIRC, Backbone.js just uses the regular jQuery handlers, so you're essentially doing the same thing, but breaking the scope :)