AngularJS : Form validation while switching tabs having it's own controller - javascript

I have created tab controls and each tab is using different controller. Each tab has some input types and also it has got validations. I have used form for each tabs and different controller for each tabs. I want to validate the form when i click on another tab. For example if there is any invalid value in Tab 1 then when user clicks on Tab 2 it should validate Tab 1 and if any invalid value is found then it should focus the invalid field and do not allow to switch tabs. Switching of tabs should be allowed only when form in the tab is valid.
Now i am not able to check whether form is valid or not during tab switch because tab DOM is out side the form and its controller. So i cant access formname.$valid property. So how i can handle this scenario?
Here is sample plunker

In plunker example, you have used different form for different template and included using ng-include. Instead you can make only one form and in individual template add only required form element instead of individual form. Use form validation on click event of specific tab and check whether input is valid or not. If input is not valid show error message and prevent switching from current tab to next tab.
Notes: Don't use ng-if for showing or hiding template instead use ng-show or ng-hide

while click on the second tab from second controller call controller of first tab and check form data is set or not.if not set call ng-click function of first tab.
we can communicate between two controllers using following ways\
1)sharing a data service
function FirstController(someDataService)
{
// use the data service, bind to template...
// or call methods on someDataService to send a request to server
}
function SecondController(someDataService)
{
// has a reference to the same instance of the service
// so if the service updates state for example, this controller knows about it
}
2)emit event on the scope
function FirstController($scope)
{
$scope.$on('someEvent', function(event, args) {});
// another controller or even directive
}
function SecondController($scope)
{
$scope.$emit('someEvent', args);
}

You can do this:
Step1: Inject the $rootScope service in the controllers.
Step2: Now make a $rootScope variable say $rootScope.validForm=false;
Step3: Now you can set/check in every controller
if(formname.$valid){
//Your code
$rootScope.validForm=true;
}else{
$rootScope.validForm=false;
}
Step4: set ng-disable="$root.validForm===false" in the html
You can play with this rootScoep variable to maipulate accordingly.
More over your structure is not correct make one form , also use ng-repeat for tab generation, need lot of improvements in your code.

Related

AngularJS: function is not a function

I have an AngularJS app, and on one of the pages, I have a number of widgets, each one displaying some information about the status of a part of the system.
I am currently working on adding the functionality to allow the user to 'hide' the heading of a given widget.
There is a 'Settings' button on the page where the widgets are displayed, which, when clicked, overlays a toolbar on top of each of the widgets. The toolbar has a number of buttons- one of which is another 'Settings' button, which opens up a dialog that allows the user to change the settings for that particular widget.
I have added a checkbox to the dialog, to enable the user to 'hide' the heading for that particular widget from view:
When the checkbox is selected on the dialog, and the user clicks 'Preview', I am expecting (eventually- I'm still working on the implementation of the feature) the heading for that particular widget to be hidden. However, currently, when the user clicks 'Preview', whether the checkbox is selected or not, I am getting an error in the console that says:
TypeError: $scope.widget.toggleWidgetHeading is not a function
This error is coming from the $scope.preview function in ctrl.js called when the 'Preview' button is clicked on the dialog:
}).controller('WidgetPickerCtrl', function($scope, $timeout, fxTag, gPresets, appWidget) {
...
$scope.preview = function(e) {
$scope.widget.toggleWidgetHeading();
...
};
...
});
I don't understand why I'm getting this console error, since toggleWidgetHeading() clearly is a function...
If I right-click on the function call above in Sublime, and select 'Go to definition', I am taken to the directive.js file where the function is defined:
.directive('appWidget', function($timeout, fxTag, appColorFilter) {
return {
...
link: function($scope, $element){
...
var toggleWidgetHeading = function(){
...
}
...
}
}
})
Also, clicking the 'Preview' button on the dialog no longer closes the dialog...
Why is it that I'm being told that this function call is not a function when it is clearly defined as one? Is the issue here something to do with the scope (i.e. the fact that I'm calling the function from ctrl.js, even though it's defined in directive.js)?
The definition of your directive, where you added ..., is actually a really relevant part about directives scopes.
Directives can implement several kind of scopes. You can actually inherit and access the parent scope, or you can have an isolated scope for example.
You may read about that in the official documentation where is well explained:
https://docs.angularjs.org/guide/directive
However, whatever will be the scope you use, by default AngularJS implements the inheritance of scopes, as usually inheritance works: children can access parent methods, but parent cannot access children's methods.
Here it seems that from the parent scope (the controller) you are trying to access the directive's scope, which is actually no possible. (even if in the link function you define the toggleWidgetHeading as private variable, and not associated to the $scope itself - but it won't work either).
So you have few options in these cases:
Define your "visible properties" inside a service and inject the service inside the directive and the controller. Then use the service in order to access and change these values, so that they will be sync between the controller and the directive
Add a scope parameter to the directive as callback & and provide a function from the controller which returns the chosen visibility of the widget, so from the directive you can call that function and get back the value of widget's visibility
Add a scope parameter as two way data binding = in the directive, which is bound to the widget's visibility of the controller, so that you have that always sync between your controller and your directive
Use events in order to communicate between the controller and the directive, broadcasting an event from the controller when the visibility changes, and reading the event from the directive getting the widget's visibility value
I hope it makes sense

Update Grid Panel on Tab Switch in ExtJS

I have two separate tabs (Tab A & Tab B). These two tabs share a single store wherein each are in their respective indices. [0] - A , and [1] - B.
My controller initializes the load upon click on its menu leaf such as:
oMe.control({
'#role-permission-grid-panel': {
edit: oMe.save,
beforerender: oMe.loadData,
},
'#roles-applicationtype button': {
click: oMe.filterByApplication
}
});
applicationtype button is the filter where tab switching of loaded data happens.
When trying to catch the value of the current filter, the controller successfully shows which one was clicked. However, this no longer refreshes the grid panel data.
SampleGridPanel /view:
console.log('Application',oController.sDefaultToggle);
Simply put, I update the data from SampleController, and try to catch it from my SampleGridPanel by calling filterApplication. My SampleGridPanel only loads the store once due to initComponent. I want to pass the updated data (in this case the selected tab) from the controller to the view. How do I notify my view that this change occurred when it only identifies data on initial load?
I'm also a bit confused. On my controller, when calling oMe.loadData beforerender, it knows the last tabbed value, but when I call oMe.loadData from a custom filter function, it no longer reloads the entire store. It only loads the filtered information based on the filter value.
You could listen to the activate and deactivate events of the tabpanel. These are also available in older versions of ExtJS.
If you are using ExtJS 6, you should really consider using ChainedStore.
A Store contains the data.
The grids aren't bound to the store, but to a ChainedStore each.
The ChainedStores have their source set to the store, and use different filter to only get the data that should be shown in their respective grid.
Please note that as of ExtJS 6.0.1 there were bugs in ExtJS that exhibited when combining ChainedStore with GroupingFeature or CellEditing. These should be fixed in ExtJS 6.0.2.

modalService cannot talk with controller and view

The code at this plnkr has a modal which pops up when a user clicks on a "Click to take quiz" button which calls a controller method that in turn calls a modal service. To get the plnkr to work, click anywhere in the code and press the space bar to add white space in a way that does not effect syntax. This will trigger plnkr to re-initialize the app and make the modal pop up after you click the button.
The problem is that the text printed in the modal does not update dynamically when timeLeft variable counts down. And also, the user's button click does not update the quizAnswer variable. In short, the modal is not able to talk interactively with the calling controller and view.
What specific changes need to be made to the plnkr to get the modal text to show the dynamic countdown, and to get the modal buttons to change the value of the $scope.quizAnswer variable?
Also, I have been carefully reading the documentation at this link. I think that the answer may be related to:
1.) $uibModal's options parameter passed in open(options) contains the parameter scope that defines the parent scope to be used for the modal's content, and also property bindToController which, when set to true, binds the scope property to a specific controller defined by controllerAs.
2.) The open(options) method returns a modal instance, which includes close(result) and dismiss(reason).
I suspect that the solution lies in these methods and parameters, but I am looking for good examples and would appreciate some experienced eyes looking at this problem.
NOTE: The solution to this came in the comments below the accepted answer, especially the link to another posting that contains 2 lines of code for emitting the modal button click's results back to the parent controller.
You have a number of issues.
First, takeQuiz at navigation.js - line 16, should be attached to $scope, not this, since this will mutate depending on context.
Second, $scope.$apply and $scope.$digst(); at navigation.js - lines 29/30 are unnecessary since you will already be in a digest cycle. They should be removed else they'll trigger an error.
Finally (and this is the meat of your issue), you are misunderstanding how modal options are bound across when creating a modal instance. It is NOT two-way binding; it is a single extends from one object to another. As a result, trying to bind to the options (or creating a concatenated string with the timeRemaining) will not update once it's bound across.
Instead, one possibility is to create an event handler inside of the modal and broadcast on each tick, updating the modal. In addition, if you pass the body text as prepend and append text, it is easier to insert your timestamp value:
You will need to inject (and broadcast from) $rootScope in your navigationController, since the modalService is registered somewhere very high in the scope chain.
On each tick, broadcast the time remaining navigation.js:
$rootScope.$broadcast('timeRemainingTick', $scope.timeRemaining);
In your modalService.js, register to receive the event inside of the controller assignment:
var timeRemainingUnbind = $scope.$on('timeRemainingTick', function(event, newTick) {
$scope.modalOptions.timeRemaining = newTick;
});
Finally, make sure that you unbind the event by calling timeRemainingUnbind() in the close events of your modal to prevent memory leaks:
$scope.modalOptions.ok = function (result) {
timeRemainingUnbind();
$modalInstance.close(result);
};
$scope.modalOptions.close = function (result) {
timeRemainingUnbind();
$modalInstance.dismiss('cancel');
};
See my working forked plunker here

Changing modal content on MVC

I have a modal, and I'like to have 2 steps on it.
The first contains a (kind of) grid with one button on each line. When the user clicks a button, I have to go to other view sending the row's id selected. He may want to go back and change his selection.
I've tried this below, but it opens at the page, not at the modal:
[HttpPost]
public ActionResult Step1()
{
return RedirectToAction("Step2");
}
Is there any way to do this?
Tks.
I'd recommend you following approaches in order get that behavior:
Javascript: your modal should contain markup for 2 steps, but only one is visible at the moment; one user clicks button on the first step - populate 2nd step dynamically with javascript
AngularJS + Route: Similar to the previous, but different from tools stand point; angular knows how to insert peaces of html into your page; see a nice article with a demo on this; this approach might require some conceptual changes to your solution, but finally you'll be sutisfied
Hope this helps.
Step1 action should return a PartialViewResult.
In your grid, upon click the link that open step1, fire a JavaScript function that does the following:
1- fire ajax request to the action passing a query string parameter, ex: ID, you can pass this parameter when you draw the link in the grid, ex: Open
2- In the action, get the parameter, retrieve the data and return the partial view.
3- when the data comes from the ajax request, set its content to the modal dialog
4- show the modal dialog, you can use either bootstrap or jquery dialog

What is the proper way to extend the behavior of a custom Angular directive?

I've written my own Angular directive called <my-table> since I use it multiple times, passing in different data to display. All of the tables have a button that when clicked, a popup form appears, like so:
However, for one of the <my-table> directives, I want to extend the behavior so that it acts slightly different from the other <my-table>s. For example, let's say that for the form that pops up, an alert box will appear when you click Submit, displaying data present in that <my-table>'s scope.
My question is, what is the best way extend the behavior of a given directive while still being able to access the directive's scope? Is this possible, or am I simply using directives incorrectly?
You can do something to this extent:
<my-table on-submit="doSomething(message)"></my-table>
In your my-table directive definition you would bind your callback to the scope:
scope: {
'submit': '&onSubmit'
},
Then in your controller you define your function:
$scope.doSomething = function(message) {
alert(message);
}
In your template, where you define the submit button, you would do:
<button ng-click="submit({message: 'bye!'})"></button>
You can reference angular's documentation for more info:
https://docs.angularjs.org/guide/directive

Categories

Resources