I've got a directive, with a field in it. The field is being passed a $scope with the model in it.
The field is ng-required="true".
Because there is a split second where the data isn't loaded into the template, there is an unsightly flash of red error text around the field.
I've tried template caching, changing the class colour, but none of it happens at the right time. What we've done in the interim is remove ng-animate. This stops the delay and flash and fade. However, this is a set back as animations would be swell in other places.
From here I will ask the concrete question:
As ngAnimate is injected in the app.js, is there a way to disable injection at a per element/controller/directive level?
As far as I know, injection cascades when it is included in the global app.js.
What I've done is set a flag to signal when the content has finished loading, and I don't show any content until that's been set to true.
On your controller
$scope.finishedLoading = function() {
... whatever logic you need to figure out if all your data is loaded ...
return $scope.myData != null;
}
Then in some part of your template that wraps the fields you want initially hidden:
<div ng-hide="finishedLoading">Please wait...</div>
<div ng-show="finishedLoading">
.. your page content here ..
</div>
Related
We are working on an HTML page which makes use of a Bootstrap tooltip on a certain <span> tag. For those who have not heard of tooltip, it is a popup of sorts which appears when hovering over the element to which it is attached. Here is a screenshot showing the <span> in question, and what happens on hover:
The premise behind adding the tooltip was that in the event that we truncate the text, the tooltip would provide an option for viewing the entire text.
However, we would now like to condtionally show the tooltip only when there is no ellipsis in the text. We defined the tooltip-enable property in the <span>:
<span uib-tooltip="{{someName}}" tooltip-placement="right" tooltip-enable="{{showToolTip}}">{{someNameShortened}}</span>
The key thing here is tooltip-enable="{{showToolTip}}", which binds the property to a scoped variable in the controller for this page. And here is the relevant (and abbreviated) controller code:
mainApp.controller('repoListController',['$scope', '$rootScope', ...,
function($scope,$rootScope, ...) {
$scope.showToolTip = false;
var repositoryList= function(){
repositoryService.getRepositoryList(function(data) {
var repoList = data;
repoList.shortenedDisplayName = repositoryService.getShortRepoName(repoList.repoName, DISPLAY_NAME_MAX_LENGTH);
// if the repository's name be sufficiently large (i.e. it has an ellipsis)
// then show the tooltip. Otherwise, the default value is false (see above)
if (repoList.repoName.length > DISPLAY_NAME_MAX_LENGTH) {
$scope.showTooltip = true;
}
});
}
repositoryList();
}]);
Based on the research I have done, the common solution for why a change to a scoped variable is not reflected in the UI is to run $scope.$apply(), or some variation on this. Running apply(), as I understand it, will tell Angular JS to do a digest cycle, which will propagate changes in the scope to the UI. However, trying to do an apply() from the code which toggles showToolTip resulted in errory. I inspected the value of $scope.$root.$$phase while running the code which updates the showToolTip variable, and the phase was digest.
So now I am at a loss to explain this. If the code is already in a digest, then why would changes not be reflected in the UI? Also, if the code is already in digest, then how could I force Angular to sync the changes to the UI?
Two things need fixing...
Don't use string interpolation for your boolean showToolTip
<span uib-tooltip="{{someName}}" tooltip-placement="right"
tooltip-enable="showToolTip">{{someNameShortened}}</span>
JavaScript variables / properties are case sensitive. In your getRepositoryList handler, you have $scope.showTooltip. It should be $scope.showToolTip (two capital "T"s)
Crappy Plunker demo ~ http://plnkr.co/edit/W7tgJmeQAJj0fmfT72PR?p=preview
I am trying to place $watch on a value in angular to setup controls differently depending on instructions from the controller and I have tried to follow the lead in angular-leaflet-directive using leafletScope.$watch("variable").
I have added a new $watch with equality comparison as it's an object:
leafletScope.$watch("controls", function(controlOpts) {...}, true)
I then call it in the controller using:
angular.extend($scope, {
controls: {}
}
This is to initialise the controls as this seems to be required.
I then call it later on an event using:
$scope.controls = { new object }
If I log the change in controls and then also on the $watch event, I get the following sequence:
control $watch event logged
$scope.control change event logged
no further logs
The fact that the watch isn't called after the $scope is changed suggests I am doing this wrong.
Can anyone advise me where, or if I have reached the wrong conclusion in my simple test.
If I do the same and change my "center" model, I get:
center$watch event logged
$scope.center change event logged
center$watch event logged with new value
This turned out to be a clash between angular-leaflet-directive and the html minifier I was using html-minifier. The angular-leaflet-directive notation for leaflet controls is controls so in my html file I had
<leaflet center="centre" controls="controls" layers="layers" width="1200px" height="800px"></leaflet>
the html-minifier took controls="controls" to be a boolean attribute and with the collapse boolean attributes flag on reduced it to
<leaflet center="centre" controls layers="layers" width="1200px" height="800px"></leaflet>
Which wasn't good. I will request this is either better documented in angular-leaflet-directive or they change the name of controls to leaflet-controls or something to avoid this happening to unsuspecting folk like me in the future.
Incidently the true flag on the watch proved to be too resource intensive and I removed it and it still worked..
Currently I am using the angular to build a prototype application, I have a list of person to show, therefor I built a model for the person. when the people 's list is loaded from the http, the model is rendered. This works well
<li class="person" ng-repeat="person in people" >
<h1>{{person.displayname}}</h1>
<div class="knockout">
<div id="person-{{person.displayname}}"></div> // to add d3.js svg
</div>
</li>
now what I need additionally is as soon as the model is rendered, to use d3.js to add another svg layer on top of the original html.
However, as the code shown below, when the model is updated, I tried to add svg for each html, but it doesn't work since at that time, the html of model is not rendered
$http.get(href).success(function(data) {
$scope.people= data.people; // update the model
var svg = d3.select('#person-'+data.people[0].name)
.append("svg");
}).error(function(data, status, headers, config) {
});
currently what I do is to set a timeout for adding the svg for each html element, but it is better for me to get notified as soon as the model 's html is rendered completely
thanks
The problem is that you people array is rendered after the success method is left. That is normal angular behavior, because their is no way in JavaScript to intercept your code. Angular checks for changes when your callbacks finished. At the time you try to append you svg stuff, non of the ids are present in the DOM.
You could use $scope.$watchCollection("people", function () { ... and render your stuff in the callback, but binding functionality to ids works against the way angular works. Try to find an angular wrapper for d3.js that you can use it without ids and as a directive.
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
I have a page where I update my model dynamically
<div ng-repeat="action in actions">
<div ng-show="action.showMyDiv">Hello </div>
<div>
when I update my model dynamically from an action in the current page with
$scope.actions[id]['showMyDiv']= true,
the div does not display.
Please , could you help me ?
Thanks in advance .
If you are updating the value manually, you can wrap it in $scope.$apply():
$scope.$apply(function(){
$scope.actions[id]['showMyDiv']= true
})
EDIT:
2 cents on $scope.$apply:
The way angular works out bindings is by doing dirty checking at every render (plenty sources about the process if you search around). As you use a angular default method (ng-click, for instance) a flag is set to warn the app that the page needs "refreshing", and you see the changes happen.
If you change anything manually, you need to warn the app that it needs refreshing, and you can do that with $apply.
See this example for clarification. Notice that two changes are made "manually", at 2s and 4s in, using window.timeout (which you shouldn't use, but rather use the $timeout service), but only after the second one are the changes propagated to the view.