I am creating a single page app with a lot of tooltips and pop ups on different areas. I am closing those elements by checking where the click is happening. for exmaple something like:
$scope.popup1 = {
open: function(){
$scope.popup1Open = true;
$timeout(function(){
$document.bind('click',function(e){
if(!angular.element('#popup1-container').find(e.target).length) {
// if in directive, ill use: $elem.find(e.target).length
$timeout(function(){
$scope.popup1.close();
$scope.$apply();
});
}
});
});
},
close: function () {
$scope.popup1Open = false;
}
}
Now like I said I have multiple popups happening, which can be opened at different areas of the page. They all have their own ids and some in their own directives. So I'm creating $document.bind('click') each time when a popup opens, and I'm not going to bother unbinding it because it will a) affect the other popups and also the popups are accessed so many times for very key areas.
So my question is, are there any performance issue that might come up with what I'm doing?
Related
I've tried to come up with some sort of "error checker/validation" for my users IF they forget to Save the edits they made on their profiles.
The user enters the Profile.html state. They start to update some of their info (i.e name, phone number, etc.). INSTEAD of pressing the SAVE CHANGES button they navigate away from the Profile state by clicking the SideMenu icon at the top left of their mobile screen.
Since the form is technically now consider to be "$dirty". I've tried to use this angular property at first but I couldn't really get the results I wanted so I tried my luck with $watch..
ProfileController.js
$rootScope.isFormDirty = false;//global variable 'isFormDirty'->inject in controller.js (toggleLeftSideMenu())
$scope.$watch('updateDriverProfileInfo', function(newValue, oldValue) {//new & oldValue = ng-model when form is 1st 'viewed' is dirty
//http://tutorials.jenkov.com/angularjs/watch-digest-apply.html
if (newValue !== oldValue) {
// console.log("updatingg")
$rootScope.isFormDirty = true;
}
}, true);
Angular docs on $watch
Maybe I should of made a factory or Service for this now that I think about it but at the time I used $rootScope so that I can set a global variable isFormDirty on this controller and use it on the General Controller that holds the Side Menu's logic in this Ionic app.
controller.js (this is where the Controller for the SideMenu is)
$scope.sidemenuIsOpen = false;
$scope.toggleLeftSideMenu = function() {//ng-click from menu.html
$scope.sidemenuIsOpen = !$scope.sidemenuIsOpen;
if ($scope.sidemenuIsOpen && $rootScope.isFormDirty) {
var confirmPopup = $ionicPopup.confirm({
title: 'Changes were not saved',
template: 'Do you want to save your changes?',
});
confirmPopup.then(function(res) {
if (res) {
console.log('Run updateDriverProfile()');
} else {
console.log('Allow user to continue w/o changes');
}
});
}
};
That's basically the gist of my code. It actually "works" but I have identified a pattern and this is where I need your assistance to either suggest a whole different method to accomplish this or perhaps some refactoring tips for this current code.
The Pop up does show when the user clicks on the Side Menu button BUT I don't think it really matters if the form is $dirty or not..
The bigger issue is that the Pop up starts showing regardless if you are trying to leave the profile.html view or any other view for that matter.
When I wrote this code I was under the impression that the Pop up and toggleLeftSideMenu functions would ONLY work on the Profile view since I am "watching" the updateDriverProfileInfo object and I also created that global variable to use between the Menu Controller and Profile Controller.
you need to have a good understanding on ionic Lifecycle, try with any of the below events
$scope.$on('$ionicView.leave', function(){
// Anything you can think of
});
$scope.$on('$ionicView.beforeLeave', function(){
// Anything you can think of
});
$scope.$on('$ionicView.unloaded', function(){
// Anything you can think of
});
find more information here http://www.gajotres.net/understanding-ionic-view-lifecycle/
Ok, so with the help of a couple sites, I was able to put this code together to inactivate the responsive coding for a website and activate the non-responsive coding. However, when the link is clicked again, it doesn't perform this function in reverse.
I tried using ".toggle", but that doesn't work. Which event should I be using to get this effect? Any help would be great!
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(document).ready(function () {
$("#togglerwd").click(function() {
$('meta[name="viewport"]').prop('content', 'width=1440');
});
});
</script>
Toggle Responsive Layout
Isn't there an event that will allow the code to be active and inactive when the hyperlink is clicked?
The click itself is the event that is happening – but that event does not know anything about what “state” your page is currently in.
A “flag” is a common programming basic, and not specific to jQuery or JavaScript. Basically it is just a variable with a value that alternates between true and false, and based on which you decide what to do.
It would look something like this:
$(document).ready(function (e) {
// set the initial flag value, we assume your page “starts” in responsive mode
var isResponsive = true;
$("#togglerwd").click(function() {
// check flag, to see what state the page is in
if(isResponsive) {
// set viewport meta to “non-responsive”
$('meta[name="viewport"]').prop('content', 'width=1440');
// remember current state in flag for check on next click
isResponsive = false;
}
else {
// set viewport meta to “responsive”
$('meta[name="viewport"]').prop('content', 'width=device-width');
// remember current state in flag for check on next click
isResponsive = true;
}
});
});
Thank you so much for your help CBroe, but I ended up creating the exact solution I wanted. Here is the code:
<script>
$(document).ready(function () {
$("#togglerwd").click(function() {
$('meta[name="viewport"]').prop('content', 'width=1440');
});
$("#togglerrl").click(function() {
$('meta[name="viewport"]').prop('content', 'width=device-width');
});
});
</script>
Full Site |
Responsive Layout
I am facing a problem while trying to click submit after re-render.
This is my view:
ShareHolderInfoView = Backbone.View.extend( {
template : 'shareholderinfo',
initialize: function() {
this.model = new ShareHolderInfoModel();
},
render : function() {
$.get("shareholderinfo.html", function(template) {
var html = $(template);
that.$el.html(html);
});
//context.loadViews.call(this);
return this;
},
events:{
"change input":"inputChanged",
"change select":"selectionChanged",
"click input[type=submit]":"showModel"
},
inputChanged:function(event){
var field = $(event.currentTarget);
var data ={};
data[field.attr('id')] = field.val();
this.model.set(data);
},
showModel:function(){
console.log(this.model.attributes);
alert(JSON.stringify(this.model.toJSON()));
}
});
This is my Router
var shareholderInfo, accountOwnerInfo;
App.Router = Backbone.Router.extend({
routes:{
'share':'share',
'joint':'joint'
},
share:function(){
$("#subSection").empty();
if(!shareholderInfo){
shareholderInfo = new ShareHolderInfoView();
$("#subSection").append(shareholderInfo.render().el);
} else{
$("#subSection").append(shareholderInfo.$el);
}
},
joint:function(random){
$("#subSection").empty();
if(!accountOwnerInfo){
accountOwnerInfo = new AccountOwnerInfoView();
$("#subSection").append(accountOwnerInfo.render().el);
} else{
$("#subSection").append(accountOwnerInfo.$el);
}
}
});
This is my HTML a div with id='subSection'.
if I check in console, I can able to see the events bound to that view.
Object {change input: "inputChanged", change select: "selectionChanged", click input[type=submit]: "showModel"}
But its not calling that showModel function afer i click submit. Please help.
Your fundamental problem is that you're improperly reusing views.
From the fine manual:
.empty()
Description: Remove all child nodes of the set of matched elements from the DOM.
[...]
To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.
So when you say:
$("#subSection").empty();
you're not just clearing out the contents of #subSection, you're also removing all event handlers attached to anything inside #subSection. In particular, you'll remove any event handlers bound to accountOwnerInfo.el or shareholderInfo.el (depending on which one is already inside #subSection).
Reusing views is usually more trouble than it is worth, your views should be lightweight enough that you can destroy and recreate them as needed. The proper way to destroy a view is to call remove on it. You could rewrite your router to look more like this:
App.Router = Backbone.Router.extend({
routes: {
'share':'share',
'joint':'joint'
},
share: function() {
this._setView(ShareHolderInfoView);
},
joint: function(random){
this._setView(AccountOwnerInfoView);
},
_setView: function(view) {
if(this.currentView)
this.currentView.remove();
this.currentView = new view();
$('#subSection').append(this.currentView.render().el);
}
});
If your views need any extra cleanup then you can override remove on them to clean up the extras and then chain to Backbone.View.prototype.remove.call(this) to call the default remove.
If for some reason you need to keep your views around, you could call delegateEvents on them:
delegateEvents delegateEvents([events])
Uses jQuery's on function to provide declarative callbacks for DOM events within a view. If an events hash is not passed directly, uses this.events as the source.
and you'd say things like:
$("#subSection").append(shareholderInfo.$el);
shareholderInfo.delegateEvents();
instead of just:
$("#subSection").append(shareholderInfo.$el);
I'd strongly recommend that you treat your views and cheap ephemeral objects: destroy them to remove them from the page, create new ones when they need to go on the page.
I'm having an issue where, when I simply console.log($scope.visitors) my data is fine, but when I console.log($scope.visitors) and then try to open a AngularUI Bootstrap dialog, the $scope.visitors.optionalFields object is empty.
Now, I've spent some time trying to replicate the issue in jsbin and I wasn't able to get it to happen, so it may be difficult to answer; just hoping somebody might have an idea what could be causing it.
So I have an array of objects like this:
$scope.visitors = [
{
company:"one",
optionalFields: {
passportNumber:"ppt",
contactNumber:"tel",
licensePlate:"1234"
},
firstName:"some",
lastName:"guy",
}
]
I ng-repeat through these with visitor in visitors and each one has a button with ng-click="editVisitorDialog(visitor)"
Now, if I make editVisitorDialog like this:
$scope.editVisitorDialog = function (visitor) {
console.log($scope.visitors);
console.log(visitor);
}
Then both $scope.visitors and visitor look good, they have all of their properties.
However, if I simply add to this function (i.e., the log calls are still at the top of the function), each visitor in $scope.visitors will have an empty optionalFields object.
$scope.editVisitorDialog = function (visitor) {
console.log($scope.visitors); // missing the optionalFields items
console.log(visitor); // missing the optionalFields items
var modalInstance = $modal.open({
templateUrl: 'app/checkin/edit_visitor/edit_visitor.html',
controller: EditVisitorController,
resolve: {
visitor: function () {
return visitor;
}
}
});
modalInstance.result.then(function (result) {
console.log(result)
}, function () {
});
}
When you call console.log() with an object it doesn't really log the object. It's rather like an inspection tool where you can see the current state, not the state at the time console.log() was called.
Try console.log(visitor.optionalFields.passportNumber) instead and I guarantee that it will log "ppt" (in your example).
That in turn means that somehwere in the added code the optional fields get lost.
I'm just getting my feet wet with Backbone, and I think I have an easy problem to solve. I have the following view which is a simple tab that when clicked opens up a panel and when closed goes back to a tab:
myApp.views.Support = {
Form: Backbone.View.extend({
initialize: function () {
this.el = $('#support');
this._ensureElement();
},
render: function () {
if (this.$el.hasClass('support-panel')) {
// close panel
this.$el.empty();
this.$el.removeClass('support-panel');
this.$el.addClass('support-button');
}
else {
// open and populate panel
var template = _.template(myApp.utils.RenderTemplate('support/default'), {});
this.$el.removeClass('support-button');
this.$el.addClass('support-panel');
this.$el.html(template);
}
return this;
},
closePanel: function () {
alert('close event fired');
},
events: {
'click #SubmitFormButton': 'submitForm',
'click #CloseSupportPanel': 'closePanel'
},
submitForm: function (event) {
alert('form submitted: ' + $('#message'));
}
})
}
Everything is working fine except that "closePanel" gets fired +2 times every time the click event happens. I assume it's some sort of cleanup I'm missing but I don't know what.
Likely its because the event is bubbling up. Try returning false.
I know this is an old question but it helped me realize what my issue was. Returning false as Daniel said works, but the root cause of my issue was having the jQuery selector twice in my markup, resulting in two jQuery objects being created thus the click event fires twice.