Refresh Model After Closing Modal Dialog in Ionic-Framework - angularjs

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?

Related

How to trigger an event in one view from a different view?

I am trying to open an Angular accordian in the header.html by clicking a button which is in the body.html. Essentially triggering an event in one view from a completely different view. Does anyone have any idea how to do this in Angular?
What you can do is using events to let your accordion directive know that something happend or use a shared service. Considering the performance, it does not make a huge difference, but only if you use $emit instead of $broadcast since the event fired via $emit bubbles up your scope hierarchy and $broadcast sends the event down. Also make sure to fire the event on the $rootScope, so it won't event bubble up anymore.
So you in case you want to use events for you could have a method on your component that fires the event via $emit on the $rootScope as follows:
function openAccordion() {
$rootScope.$emit('on-accordion-open', null);
}
You could then use this in your view, e.g. in body.html. Remember that function above is part of another directive / component or controller.
<button ng-click="vm.openAccordion()">Open Accordion</button>
Also note that I assume you are using controllerAs syntax (set to vm).
In your accordion directive you can then hook up listeners to several events for example the on-accordion-open:
$rootScope.$on('on-accordion-open', function() {
// Open the accordion
});
The other soltuion is to use a shared service. In this case I would create a AccordionServce that is aware of all instances of accordions. The service could look like this:
angular.module('myApp').service('AccordionService', function() {
var accordions = {};
this.addAccordion = function(name, accordion) {
accordions[name] = accordion;
};
this.removeAccordion = function(name) {
delete accordions[name];
};
this.getAccordion = function(name) {
return accordions[name];
};
});
In your accordion's controller you then add the accordion to the AccordionService via
accordionService.addAccordion('myAccordion', this);
The this in the snippet above is refering to the accordion controller. Thats important because if you then get an accordion in your component in the body.html, you'll get the controller instance and can call methods like open.
So in your body component you can then inject the AccordionService and get the accordion to call a method:
accordionService.getAccordion('myAccordion').open();
Make sure to define open on the accordion's controller.

Propagating model changes to a Parent Controller in Angular

I have a an angularJS application and would try to simulate a Asana.com feature.
The scenario is the following:
I have a MainController for my application in the body tag, and inside this controller I populate my names in my sidebar:
.controller('MainController', ['$scope', 'NamesService', function($scope, NamesService) {
$scope.names = NamesService.query();
...
};
When I click on any name (for example, Anna), my application change the route and in inject the name-edit.html template in my ng-view, represented by the content area on the picture above. I have a input used to change from Anna to Carol and a update button. When I hit the update button, It fires a function that updates in my database, changes Anna to Carol in my content area (represented by the yellow arrow position) but doesn't change the red arrow position in my sidebar.
I tried to call the following code again inside my success update callback, but doesn't work
$scope.names = NamesService.query();
I'd like to know how to propagate the child controller to the parent controller, changing Anna to Carol inside $scope.names. Is it possible to do this, without reloading $scope.names?
You could use event system of Angular using $scope.$emit('eventName', eventData) un your child controller to pass data up on the hierarchy.
//child, assuming you have promise callback
NamesService.query().then(function(data){
$scope.$emit('eventName', data)
})
And in your parent controller have the following
//parent
$scope.$on('eventName', function(event, data){
$scope.names = data;
})

Angular JS Parent Scope Click Handler Not Destroyed

This is my html
<div controller="parent">
<button ng-click="doSomething()"><Click</button>
<div ng-view></div>
</div>
Here is my js controller for a particular view:
function child($scope) {
$scope.$parent.doSomething = function() {
// do http request or seomthing else
}
}
Now when Im on the child controller view, angular does http request when i click on the button on the parent scope but when I navigate to other views with different controllers (of course), angular still does http request when I click on the button on the parent scope.
I thought when the view changes, the controller and including its scope will be destroyed and thereby effectively removing references to that particular scope objects.
My work around is to listen for $scope.on('$destroy') and override the function to return empty or something else.
Is there a better way to do this?
Thanks
$scope.$parent.doSomething is adding the doSomething property to that parent $scope object. So long as that parent scope doesn't get destroyed, your click handler will stick around.

AngularJS Directive-Controller-Service Interaction

I'm trying to design a single page pagination app that displays the different pages of a document beneath each other in the same window. It has to meet the following requirements:
A pagination toolbar where the user can click next/previous/... and submit a page to go to.
The window scrolls to the right page of the document after a page has been submitted
If the user scrolls manually, the current page should update automatically
I tried some different stuff but was never really satisfied with the result. This is how I see the solution:
The app consists of 1 factory:
DocumentFactory: Stores the current page of the document and has the following methods:
setPage(page): sets the page in a factory so different controllers/directives can use this page
broadcast(pageChanged): broadcasts an event after the page has changed so the controllers/directives can listen to this even and react approprialty
2 controllers:
PaginationCtrl[DocumentFactory]: The pagination toolbar controller, updates the page by calling the setPage(method) of the DocumentFactory and listens to the pageChange event to update it's own scope when the page changes in an other controller/directive
DocumentCtrl: The controller of the document
1 Directive:
Page[DocumentFactory]: Resembles a page in the document and has the following methods/listeners
scrollToPage(): If the currentPage equals this pages number (added to the directive as an attribute, scroll to this page)
If this page is visible and the highest in the window of all visible pages, change the current page to this page's number by calling the DocumentFactory setPage(page) method.
Is this the right approach to store the page in a service and use events for the other controllers/directives to listen to it?
Should I create a controller in the directive to listen to the event or add a $watch in the link function to watch for changes in the current page (inherited from the parent Ctrl scope)?
Should I let each directive check if it's page number equals the current page on page change or should I let the DocumentCtrl scroll to the right element?
AS you already have the methods in the controller calling the factory, what you need is to use the '&' isolate scope in the directive to call the method you want.
Create the methods in the controller
var app = angular.module('app', []);
app.controller("Ctrl", function ($scope) {
$scope.goForward = function (){
alert("call to the paginator page up service");
};
$scope.goBack = function (){
alert("call to the paginator page down service");
};
});
Then set up the '&' scope isolates in the directive:
app.directive('paginator', function (){
return{
restrict: 'E',
scope:{
forward: '&',
back: '&'
},
template: '<button ng-click="forward()">Page up</button>' +
'<button ng-click="back()">Page down</button>'
}
});
Finally add the directive to the page with your attributes as defined in the directive:
<paginator forward="goForward()" back="goBack()"></paginator>
Here's the code in Plnkr.
HTH

Multiple Views bound to a single element in backbone.js

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);
}
});

Resources