Background:
I have an AngularJs wizard like app with 3 steps. The wizard steps contain 4 uiRouter states that share a single controller. The first state is an abstract view containing bootstrap pill navigation with a ui-view as a placeholder for the other 3 nested states. The 3 nested states have a separate views that contain uniquely named forms. I would like to control the pill navigation in the the parent state by checking if the current visible form is valid; if the form is invalid then I would hide/disable the ability for the user to click on the next navigation pill.
Note: I'm using the "controller as" syntax declared in the uiRouter states.
Issue:
I don't seem to be able to get a reference to the current viewable form from within the controller.
Here's what I've tried:
1. passing in the reference to the form visible form in an init call --> gets undefined error when I try to write to console
2. renaming the forms and even passing in the scope reference as described in this link:
http://www.technofattie.com/2014/07/01/using-angular-forms-with-controller-as-syntax.html
Demo:
Here's a plnkr link that shows intent:
http://plnkr.co/edit/YUnwoBD2dt4bzbfujJSW
Controller code:
(function () {
'use strict';
angular
.module('recquisitionManagement')
.controller('RecquisitionEditCtrl', [RecquisitionEditCtrl]);
function RecquisitionEditCtrl() {
var vm = this;
// get ref to request form
vm.setRequestForm = function(form) {
vm.requestForm = form;
console.log(form);
// tried to call from form ng-init --> undefined response
}
// console.log($scope.vm.requestForm);
// console.log(vm.requestForm);
vm.recquisition = { id: 0, name: 'some name', date: 'date here' };
vm.requestFormIsInvalid = true; //this.requestForm.$valid;
vm.approvalFormIsInvalid = true;
if (vm.recquisition && vm.recquisition.id > 0) {
vm.title = 'Edit';
} else {
vm.title = 'Create';
}
}
}());
Any suggestions on how to get the needed form reference?
Multiple options are explained here: Can I access a form in the controller?
Also it can be good to rename functions that share the name of the controller and try to use the common angular practice.
yes like so:
<form name="vm.theFormName">
you can then access it via vm
Bit late now I realise but you were passing the ctrl as an array, needed to change this:
.controller('RecquisitionEditCtrl', [RecquisitionEditCtrl]);
to this:
.controller('RecquisitionEditCtrl', RecquisitionEditCtrl);
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/
I have a activity state, and when there are no activities I would like to display a message. So I created a if/else statement that checks if the $scope activities has any content, if not it injects a certain code into the template.
if(!$scope.activities.length){
var empty = function(){
$scope.renderHtml = function (htmlCode) {
return $sce.trustAsHtml(htmlCode);
};
$scope.body = '<div>There are no activities yet, <a ui-sref="home.users">click here to start following some friends!</a></div>';
}
empty()
}
The problem is that ui-sref doesn't work, a normal 'a href` does work though. Are there any solid work arounds for this problem?
To get this work I created a element with ng-show,
%div{"ng-show" => "activitiesHide"}
And this js,
activitiesService.loadActivities().then(function(response) {
$scope.activities = response.data;
if(!$scope.activities.length){
$scope.activitiesHide = response.data
}
})
I place the results from the service in the activities scope, and then check in the js if it has content. If not activate the activitesHide show.
I have a view with an ng-grid, like this:
<div class="ng-grid-style" ng-grid="customersGrid"></div>
In the controller of this view I keep a list of customers and a list of selected customers:
$scope.customers = [];
$scope.selectedCustomers = [];
I also do the binding of the grid with these two:
$scope.customersGrid = {
data: 'customers',
selectedCustomers: $scope.selectedCustomers
};
I also fetch customers from a URL at the start of the controller instance:
$scope.fetchCustomers() = {
// with $http I bring customers...
// (...)
$scope.customers = response.customers
};
$scope.fetchCustomers();
In the view I have links to other routes of the same app. These routes associate other views controlled by other controllers.
My problem is that if I navigate from the view containing the ng-grid to another view, and then I use the back button, I lose the state of $scope.customers and $scope.selectedCustomers.
This app is an SPA, so I should be able to keep these two in memory (or elsewhere) along the life of the app instead of having them in the $scope
What is the most recommended way of storing these two? I've read that I could use localStorage, sessionStarge, a service, etc. Also I'm not sure how the binding works when NOT USING the scope instead. Can I pass to the grid's data something that is not in the scope? eg:
$scope.customersGrid = {
data: localStorage.customers, // possible?
selectedCustomers: localStorage.selectedCustomers // possible?
};
In my controller I want to use certain variables in several places.
For example I got a form with few fields (combo / textfield) and I want to use a link to them in a various places of my controller code. How can / should I declare such variable?
Usually I use:
refs: [
{
ref: 'myCombo',
selector: 'myPanel combo[name=myCombo]'
},
{
ref: 'myTextfield',
selector: 'myPanel textfield[name=myTextfield]'
}
]
But is it ok to use getMyCombo() / getMyTextfield() every time I have to work with this fields in my controller?
The "refs" feature of the controller is really just generating getter functions for you by using Ext.ComponentQuery with the provided CSS selector. The way you're using them is one way you can make use of the system, though you can also use refs to instantiate (for example) views for the controller using their configured alias or xtype. In your example, you're saving yourself the hassle of re-writing some long-ish ComponentQuery calls.
The 'autoCreate' option, although not documented, is great for this type of thing if for example you wanted to always instantiate a new instance of a certain object every time the controller is activated, you could do so in the init() function.
The answer posted here demonstrates using refs to create new instances and further explains the functionality of autoCreate and forceCreate options.
If you want to use an object or some variable throughout your controller, just set a property on the controller, most suitably in the init method...
Ext.define('App.controller.Messaging', {
/** Include models, stores, views, etc... */
refs: [{
ref: 'messageBox', // creates magic method "getMessageBox"
xtype: 'my-messagebox', // in the class file: alias: 'widget.my-messagebox'
selector: '', // could be itemId, name, etc. Same rules as a CSS selector
autoCreate: true // automatically create when "getMessageBox()" is called
}],
/** I always initialize controllers as-needed, passing the application object */
init: function(app){
var me = this;
// Initialize whatever you need, maybe set up some controller properties
this.eventBus = app.getEventBus();
this.user = app.getActiveUser();
// I prevent listeners from being established twice like this..
if(this.inited === true)
return;
this.inited = true; // nothing past this line will be executed twice
// Observe view events
this.control();
// Listen for application events
app.on({
'getMessages': {fn: me.showMessageBox, scope: me}
});
// Specific controller events
this.on({
'someEvent': {fn: me.someFunction, scope: me}
});
},
// A function using controller properties defined in the init() method
someFunction = function(){
var me = this; // controller
// Lets show the active user
console.warn("Active User", me.user);
// And then fire an event, passing this controller
me.eventBus.fireEvent('somethingHappened', me);
},
// Invoked on the application event "getMessages"
showMessageBox: function(sessionId){
var me = this; // controller
/** ... Load the messages for the provided sessionId ... */
// Then create an instance of the message box widget as property on the controller
me.messageBox = me.getMessageBox({
/** pass config to the view here if needed */
});
}
});
I have two different backbone js file for 2 different view. I need to call the render method of the second js file from the first one. How can i do that
I have one backbone.js file which as a view called DocumentsPageView. In my second backbone js file when i click button on the first js file i have to call the render method of DocumentsPageview
first js file
first.backbonejs = (function($) {
case myapp
sectionView = new second.mysecondbackbone.DocumentsPageView();
sectionView.render();
break;
}
}(jQuery)
second js file
second.mysecondbackbone = (function($) {
var DocumentsPageView= Backbone.View.extend({
render: function(){
//render the page
}
});
}(jQuery)
I am getting object undefined in the declaration section
Thanks & Regards
Ashik
My advice is, don't.
Use a mediator object that sits between the two, and controls the process of working with both views.
It can be as simple as this:
myProcess = {
show: function(){
var view1 = new View1();
view1.on("foo", this.doMoreStuff, this);
this.showView(view1);
},
doMoreStuff: function(){
var view2 = new View2();
this.showView(view2);
},
showView: function(view){
// code to stuff view.$el in to the DOM
}
}
The advantage here is that you have a high level workflow that can be managed and maintained on it's own, separate from the implementation details of the individual views. You don't have to trace down in to the individual views to see how they work together.
I wrote more about this, here: http://lostechies.com/derickbailey/2012/05/10/modeling-explicit-workflow-with-code-in-javascript-and-backbone-apps/