Opening angular-ui-bootstrap modal from javascript - angularjs

I am trying to open the modal dialog from javascript when a certain condition is met. The example shown here http://angular-ui.github.io/bootstrap/ invokes the modal on ng-click.
How do I achieve showing modal when a certain condition is met?
Thanks
Additional Info
Sorry didn't mean to create confusion. I am going to clarify a little bit more by saying what I meant by "certain condition". The scenario is the user will search for customer by name and will get a list of customers back matching with the search string. The user then clicks on any one of the customer row to get more detail information.
When clicked the code control is handed off to Controller (It's an ASP.Net MVC app) which will then go through other classes and finally get data of the customer from database. It will then populate a boolean property called spouseNotFound. It then returns the JSON back to angularjs controller. Now assume that if this particular customer does not have spouse I want to show a modal saying that "Spouse not found".
So, no, I don't want the modal to be invoked on an event, rather than on business rule condition.
Hope that helps to understand things clearly.

Straight from the documentation.
$modal is a service to quickly create AngularJS-powered modal windows. Creating custom modals is straightforward: create a partial view, its controller and reference them when using the service.
Example plnkr. (http://plnkr.co/edit/?p=preview)
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
items: function () {
return $scope.items;
}
}
});

If anyone needs an easy approach to showing Bootstrap Modals with AngularJS, then you can simply use the following approach. It worked well for me:
var element = angular.element('#myModal');
// Open dialog
element.modal('show');
// Close dialog
element.modal('hide');

What you may be looking for is a $watch on your scope. This will allow you to monitor a variable, and when the value is what you want, then launch the modal as Nix describes.
$scope.$watch('entities', function(){
if($scope.entities[0].checked && $scope.entities[1].checked){
alert("If I were bootstrap, I'd be launching a modal right now!");
};
}, true);
A jsfiddle: http://jsfiddle.net/HB7LU/1636/

Related

Angular / modal /ngDialog - How to have multiple controller in a modal with nested / multiples states / views?

I used ngDialog and have a modal with nested views. Insight my modal I am switching between signup (state) and login (login) so I need to include controller for signup and one for login. I have been searching for a solution for hours everywhere but couldn't find anything. Does anyone how I can do that? In case somebody knows a solution NOT for ngDialog but for $modal, please let me know too. Thanks!!
$scope.clickToOpen = function () {
ngDialog.open({
template: 'js/modal/modal.html',
controller: 'SignupCtrl',
});
};
In the code above- everything works find for signup because I included its controller, but not for Login as I don't know how to include logins controller.
Also, if its important, I will include more code-just thought it doesn't matter for my question.
What I also tried is this:
app.config(function ($stateProvider) {
$stateProvider.state('activity.modal', {
url: 'modal',
templateUrl: 'js/modal/modal.html',
views: {
'loginView#': {
templateUrl: 'js/login/login.html',
controller:'LoginCtrl'
},
'signupView#': {
templateUrl: 'js/signup/signup.html',
controller:'SignupCtrl'
}
}
});
});
but that doesn't work neither for login nor for signup.
There are multiple solutions to this problem. Some of them are better than others, but at the end of the day, it depends on what kind of UI/UX you are looking for.
One Dialog with Merged Controllers (arguably the worst approach)
You can use only one dialog with one template, but you have to merge the controllers together so the new controller can handle both login and sign up logic.
Two Different Dialogs
You can open different ngDialog pop-ups - one for login a different one for sign up. These would be triggered by different buttons in your view.
For example having two buttons (Login and Sign Up) where the Login button would show the Login dialog and the Sign Up button the Sign Up dialog.
Use Components in Your Dialog (arguably the best approach)
You can open one ngDialog instance and in the template, you could use two different components - one for login and one for sign up. Each component would have its own separate template and controller and thus the rule of separation of concerns still applies.
The added benefit of the last approach is that you can then use these components anywhere on your website - not just the ngDialog template.

Angular - How can I clear factory variables when the view changes?

I'm fairly new to angular, so bear with me. :-)
I have a list of contacts in one view. I put together a service with some setter and getter functions to hold the contact ID. When the user clicks a contact on the list view, it sets the ID in the service and moves to the edit form view. This is working well, but when I'm done editing and click to go back to the list view I want to clear the variables in the service. How would I do that?
I found one other answer on this topic, but I think it's using the built in angular router instead of ui-router.
Angular updating and clearing factory variables
Also, wouldn't the method in that answer also clear the variables when the user moved from the list to the edit form in the first place?
Thanks!
If it's a route change you can use $locationChangeStart, see $location
For example in your edit controller, register for the event on route change:
var onloc = $scope.$on('$locationChangeStart', function (event, newUrl, oldUrl) {
myService.cleanUp(whatever);
onloc();
});

Angular modal window depenging on server answer

Good day! I am rather new to angular and I need help in creating a directive or, maybe, some other solution. I have got a list of news, which are displayed one after another. It is done by ng-repeat. Inside each new there are a things like creationDate, post.media and etc. I would a new to appear in a modal window, when user clicks directly on the div with a new text. But, i would to make a request to the server after click to get the most recent version of the new.
So, this is what I want briefly:
1) User clicks on new's text.
2) The id is sent to the server.
3) Server responds with all post information
4) Some specified template loads with all information got from server and appears in a modal window.
What i tried to do:
I tried to create a directive and placed it on new's text. I created an isolated scope, which expects to have one more attribute, so I could get a postId.
scope: {
postId: '#'
}
I created a template and specified a link to it as templateURL.
Then i created a link function and inside it i created smth like this :
element.on('click', function() {
scope.postInfo = scope.findPostById(postId);
});
But for now, this directive just replaces its innerHTML :))
Some requirments:
Modal window should appear only after server receives all the information.
Thank you :)
Basically ng-repeat creates scope for every single element. So you just need to create a function i.e.
$scope.fetchNews = function(news){
newsRepository.find(news).then(function(result){
$modal.open({
templateUrl:...
controller:...
resolve:{
news:function(){
return result;
}
}
})
})
}
And in the controller of the model you inject as depenency news and after that you are done :)

Angular modal dialog best practices

What is the best practice for creating modal dialogs with dynamic content, contrasted with dialogs that don't have dynamic content.
Eg..
We have some modal forms that accept a list of form elements, and have submit/cancel.
Also, there are modal dialogs that just display a confirm/ok type of operation.
I've seen a lot of people saying that dialogs should be services passed into the controller, but it seems to me that services shouldn't be rendering UI components and manipulating the DOM.
What is the best practice for assembling these two types of dialogs? Thanks.
Angular UI Boostrap provides a service - $dialog - that can be injected wherever you need to use a dialog box. That service has two main methods: dialog and messageBox. The former is used to create a dialog with dynamic content and the latter to create a message box with a title, a message and a set of buttons. Both return a promise so you can process its result, when it's available.
I think this approach works well, because it fits the somehow natural, imperative way of handling dialogs. For instance, if the user clicks on a button and you want to show a dialog and then process its result, the code could look like this:
$scope.doSomething = function() {
$dialog.dialog().open().then(function(result) {
if (result === OK) {
// Process OK
}
else {
// Process anything else
}
});
}
You can indeed use directives to do the same, and perhaps it seems the right way to do it since there is DOM manipulation involved, but I think it would be kind of awkward to handle it. The previous example would be something like this:
<dialog visible="dialogVisible" callback="dialogCallback()"></dialog>
...
$scope.doSomething = function() {
$scope.dialogVisible = true;
}
$scope.dialogCallback = function(result) {
if (result === OK) {
// Process OK
}
else {
// Process anything else
}
}
IMO, the first example looks better and it's easier to understand.
Since dialogs are DOM components, they should probably be directives. You can either build up the DOM elements of the modal inside the directive itself or put the elements on the main html page hidden and unhide them from the directive. If you don't isolate the directive's scope, you can just refer to the controller scope (unless you are in a child scope) from the directive.
Dynamic vs. static content isn't that much of a decision point IMO. Since you have access to the scope from within the directive, you can access whatever you need from the inherited scope.
One quite simple design that works well is to :
Have such a "modal dialog" div somewhere in your html. It will be typically absolute, taking all the screen width and height (typically a dark translucent div with a smaller dialog div into it) and not displayed by default (use ng-show to display it conditionally, depending on the existence of modals or not)
Declare a controller that listens to dialog events ("dialogShow", "dialogClose", etc.) and change its "currentModal" $scope value when receiving them. According to the ng-show condition setup in the previous step, the modal will accordingly display or change or disappear (if set to null/undefined)
Trigger dialog events from anywhere in your application, using broadcasts.
Improvements are:
Events parameters properties (setup when triggering and received by the controller) could include title, message, images, even html (to be sanitized), buttons, callbacks for those buttons, display durations (throught $timeout)
Remember a stack of received alerts. When one is closed, the next pending one displays

Updating URL in Angular JS without re-rendering view

I'm building a dashboard system in AngularJS and I'm running into an issue with setting the url via $location.path
In our dashboard, we have a bunch of widgets. Each shows a larger maximized view when you click on it. We are trying to setup deep linking to allow users to link to a dashboard with a widget maximized.
Currently, we have 2 routes that look like /dashboard/:dashboardId and /dashboard/:dashboardId/:maximizedWidgetId
When a user maximizes a widget, we update the url using $location.path, but this is causing the view to re-render. Since we have all of the data, we don't want to reload the whole view, we just want to update the URL. Is there a way to set the url without causing the view to re-render?
HTML5Mode is set to true.
In fact, a view will be rendered everytime you change a url. Thats how $routeProvider works in Angular but you can pass maximizeWidgetId as a querystring which does not re-render a view.
App.config(function($routeProvider) {
$routeProvider.when('/dashboard/:dashboardId', {reloadOnSearch: false});
});
When you click a widget to maximize:
Maximum This Widget
or
$location.search('maximizeWidgetId', 1);
The URL in addressbar would change to http://app.com/dashboard/1?maximizeWidgetId=1
You can even watch when search changes in the URL (from one widget to another)
$scope.$on('$routeUpdate', function(scope, next, current) {
// Minimize the current widget and maximize the new one
});
You can set the reloadOnSearch property of $routeProvider to false.
Possible duplicate question : Can you change a path without reloading the controller in AngularJS?
Regards
For those who need change full path() without controllers reload
Here is plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');
I realize this is an old question, but since it took me a good day and a half to find the answer, so here goes.
You do not need to convert your path into query strings if you use angular-ui-router.
Currently, due to what may be considered as a bug, setting reloadOnSearch: false on a state will result in being able to change the route without reloading the view. The GitHub user lmessinger was even kind enough to provide a demo of it. You can find the link from his comment linked above.
Basically all you need to do is:
Use ui-router instead of ngRoute
In your states, declare the ones you wish with reloadOnSearch: false
In my app, I have an category listing view, from which you can get to another category using a state like this:
$stateProvider.state('articles.list', {
url: '{categorySlug}',
templateUrl: 'partials/article-list.html',
controller: 'ArticleListCtrl',
reloadOnSearch: false
});
That's it. Hope this helps!
We're using Angular UI Router instead of built-in routes for a similar scenario. It doesn't seem to re-instantiate the controller and re-render the entire view.
How I've implemented it:
(my solution mostly for cases when you need to change whole route, not sub-parts)
I have page with menu (menuPage) and data should not be cleaned on navigation (there is a lot of inputs on each page and user will be very very unhappy if data will disappear accidentally).
turn off $routeProvider
in mainPage controller add two divs with custom directive attribute - each directive contains only 'templateUrl' and 'scope: true'
<div ng-show="tab=='tab_name'" data-tab_name-page></div>
mainPage controller contains lines to simulate routing:
if (!$scope.tab && $location.path()) {
$scope.tab = $location.path().substr(1);
}
$scope.setTab = function(tab) {
$scope.tab = tab;
$location.path('/'+tab);
};
That's all. Little bit ugly to have separate directive for each page, but usage of dynamic templateUrl (as function) in directive provokes re-rendering of page (and loosing data of inputs).
If I understood your question right, you want to,
Maximize the widget when the user is on /dashboard/:dashboardId and he maximizes the widget.
You want the user to have the ability to come back to /dashboard/:dashboardId/:maximizedWidgetId and still see the widget maximized.
You can configure only the first route in the routerConfig and use RouteParams to identify if the maximized widget is passed in the params in the controller of this configured route and maximize the one passed as the param. If the user is maximizing it the first time, share the url to this maximized view with the maximizedWidgetId on the UI.
As long as you use $location(which is just a wrapper over native location object) to update the path it will refresh the view.
I have an idea to use
window.history.replaceState('Object', 'Title', '/new-url');
If you do this and a digest cycle happens it will completely mangle things up. However if you set it back to the correct url that angular expects it's ok. So in theory you could store the correct url that angular expects and reset it just before you know a digest fires.
I've not tested this though.
Below code will let you change url without redirection such as: http://localhost/#/691?foo?bar?blabla
for(var i=0;i<=1000;i++) $routeProvider.when('/'+i, {templateUrl: "tabPages/"+i+".html",reloadOnSearch: false});
But when you change to http://localhost/#/692, you will be redirected.

Resources