I have a main view and a modal window which is a child view from the parent view.
The modal consist of a form which is submitted via ajax.
How do I render the parent view from the child view after submitting the form?
The parent view can bind to notifications on the child view to know when the form was submitted. Then it can render itself:
On the parent view:
// Parent initialize
this.childView.on('formSubmitted', this.render, this);
// Parent removal
this.childView.off('formSubmitted', this.render, this);
On the child view:
// After form is submitted
this.trigger('formSubmitted');
The child will trigger a "formSubmitted" event and the parent's render function will be called.
Related
I am writing an Ionic app, and I want to run a function to refresh the view's model whenever the view becomes visible. I use $ionicView.enter in my parent view's controller to load the model normally like so:
//Code in Parent View's Controller
$scope.$on('$ionicView.enter', function(e) {
loadModel();
});
On the view I have a button to open an ionic modal dialog that has its own controller where the user can add a new entry to the main view's model. After the user saves the new entry, I close the modal and would like for the model to automatically be refreshed like so:
//Code in Modal Dialog's Controller
$scope.addEntry = function() {
//code to add new data to model
//need to call loadModel() in parent controller here
$scope.modal.hide();
};
The event in the parent view's controller does not fire after the modal dialog is closed. What is the best way to call loadModel() in the parent view's controller from the modal dialog's controller?
I have the following item view:
return Marionette.ItemView.extend({
template:tpl,
tagName: 'div',
className: 'v_itv_record_type_item',
events:{
'click #ui.item':'itemClicked'
},
ui:{
item:'.v_itv_record_type_item'
},
itemClicked:function(e){
console.log('clicked');
}
});
that uses the following handlebars template:
<div class="clicktarget">
Stuff Goes Here
</div>
If you click on one of these item views, it does not register the click event. I understand that Backbone restricts access to just the views slice of the DOM, but apparently this does not extend to the containing div itself, even though that containing div is not part of any template, parent view or otherwise.
If we change the ui hash and point item at .clicktarget the click is registered. But this gives me me a <div><div>stuff goes here</div></div> structure for seemingly no reason. Is this the only way to detect a click on the entirety of an item views DOM element?
You can register a click event on the view element by omitting the selector:
events:{
'click' :'itemClicked'
}
Note that if you have an event handler at view level, all the clicks inside the view will bubble up and trigger it's handler, unless it was stopped (event.stopPropagation()) on the way. This is the expected behavior.
I need to add some data in teh child controller, after I submit added data in the child controller,I need parent controller reload the data. Here is some code sample:
function parentCtr(){
$scope.getReportsView = function() {
ReportService
.getReportListView(
$scope.Headerinfo.expenseReportID)
.then(
function(reportInformation) {
$scope.Expensereports = reportInformation.expensesList.expReport;
if ($scope.Expensereports != null && $scope.Expensereports.length > 0) {
$scope.noExpense = false;
}
});
};
}
in the child controller:
I have function like:
$scope.addData = function(data){
addDataService.add(data).then(function())
}
I am using bootstrap $modal to create add data popup, the child controller is for thsi modal:
<div ng-contrlooer='parentCtr'>
{{Expensereports }}
<button ng-click='openPopup'>Add Data</>
</div>
waht I want is after I add data successfully in the child controller, I need to let $scope.getReportsView() in the parent controller run again to refresh table.
The added data is part of $scope.Expensereports which is I used in parent controller.
Ok, after seeing your code and your HTML, and with your explanation of how you're calling the child controller in a modal, I think what's going on here is while you logically have a parent child relationship, no such relationship exists between the controllers. If one does, then `$scope.$parent should give you access to the parent scope.
Since that doesn't seem to be the case, my suggestion would be simply to call $scope.getReportsView from the "parent" scope when you return from the modal.
In the Marionette docs for CompositeView, it describes overriding appendHTML to customize the target the HTML of each itemView should be added to using jQuery's append.
Using a similar line of thinking, if I have two ItemView objects, a Parent and a Child, and Parent's template has an empty target <div class="target"></div> I'd like to inject Child's template inside of. I can use jQuery's html with an onRender in Parent to do this.
Throughout the course of the Parent's life, I want to flush out or replace the contents of .target with something else. If I later want to re-render Child into .target (since it may render it's template differently based on user interaction since the first rendering), I can use the same logic:
Render the Child template
Set .target's contents to Child's el with jQuery's html
The issue is, once this second rendering of Child has occurred, any events from the Child view seem to be lost.
Here is a concrete example of what I'm describing:
The HTML
<script type="template" id="parent">
<p><a class="re-render" href="javascript://">Re-Render</a></p>
<div class="target"></div>
</script>
<script type="template" id="child">
<p>The Link</p>
</script>
<div id="main"></div>
The Javascript
var Views = {};
Views.Child = Backbone.Marionette.ItemView.extend({
template: '#child',
events: {
'click a': 'changeBg'
},
changeBg: function() {
this.$el.css('background-color', '#'+Math.floor(Math.random()*16777215).toString(16));
}
});
Views.Parent = Backbone.Marionette.ItemView.extend({
template: '#parent',
childView: undefined,
ui: {
target: '.target'
},
events: {
'click .re-render': 'onRender'
},
initialize: function() {
this.childView = new Views.Child();
},
onRender: function() {
this.childView.render();
this.ui.target.html(this.childView.el);
this.childView.$el.css('background-color', 'transparent');
}
});
var App = new Backbone.Marionette.Application();
App.addRegions({
mainRegion: "#main"
})
App.mainRegion.show(new Views.Parent());
Clicking The Link in Child at first works great. Once Re-Render is clicked in Parent, the Child click event is never re-applied to the newly rendered version of the Child's template. How can I ensure the Child template's events will be re-applied each time the Child is rendered?
You can find a jsFiddle for this here.
You should call .delegateEvents() on the child view after it has been re-rendered:
onRender: function() {
this.childView.render();
this.ui.target.html(this.childView.el);
this.childView.delegateEvents();
this.childView.$el.css('background-color', 'transparent');
}
This is how to solve it in vanilla Backbone. I don't know if Backbone.Marionette has another way of handling the problem, but in Marionette terms you could add the delegateEvents call to the child view's onRender method to encapsulate the re-renderability.
Views.Child = Backbone.Marionette.ItemView.extend({
//...
onRender: function() {
this.delegateEvents();
}
});
I am trying to bind two click events to a single HTML element in two different views. One of the views triggers the event, the other does not.
One of the view has body as its el attribute. If I change this view's el to the same element as the other view's, then both events get triggered.
Is this expected? How can I bind click events for the same element in two different views?
Yes, this is expected. Backbone uses jQuery delegates for the event binding. Which means, the event is actually bound to the view's EL, not directly to the child node.
When you say, "the same element", do you mean literally the exact same node in the DOM? Or, do you mean a node with the same selector? I guess I'm not entirely clear.
can i ask why you want to have 2 views binding to the same element?
from my point of view, you should only have 1 view that represents the element itself
and event's bound to an element should be defined in that view only.
you will run into trouble when you are binding click events to elements that don't belong to the view
if you bind trough the delegateEvents hash, these events are contained within the el of the view.
if you are however defining the click yourself, your code becomes less managable.
so, on to what you can do:
events!
you can define 1 view, holding your button and trigger an event when the button is clicked, while other views that need to handle some code when that button is pressed don't bind directly to the button click itself, they can listen to that raised event.
example on jsfiddle:
http://jsfiddle.net/saelfaer/Qck5w/2/
the gist of it in code here:
// an event aggregator object to trigger and bind to
var events = _.extend({}, Backbone.Events),
// two views that talk to each other trough the event aggregator
var myButtonView = Backbone.View.extend({
// first view binds a click event to the button
events: {
"click a" : "myClickEvent"
},
initialize: function(){
_.bindAll(this, "render");
this.render();
},
render: function(){
return this;
},
// click event executes this function, which triggers a custom event on the events object.
myClickEvent: function(e){
$(e.target).blur();
events.trigger("sidebar:myCustomClickEvent");
return false;
}
});
var myPanelView = Backbone.View.extend({
// second view binds to that event, and executes the custom click handler
initialize: function(){
_.bindAll(this, "render", "myClickEventHandler");
events.bind("sidebar:myCustomClickEvent", this.myClickEventHandler);
this.render();
},
render: function(){
return this;
},
// the click handler does some logic (appends div) when the event is raised.
myClickEventHandler: function(){
var txt = $('<div/>').text("you just clicked the button. (bound in other view)");
$(this.el).append(txt);
}
});