ng-repeat when data is ready - javascript

I have a website with two webpages:
A settings page: webpage with JSON content that looks like this:
{"Sources":["1","2","3","4"]}
A webpage that present all the known "sources" in the system. Just print them one after another.
When my application loading, I downloading the sources page, and save in on my scope at $scope.Settings.Sources.
My AngularJS code:
$scope.getSettings = function (ID, successCallback, failureCallback)
{
$scope.ID = ID;
$http.get("/sources?ID=" + ID).then(function (response)
{
if (response.status == 200)
{
$scope.Settings = response.data;
successCallback();
}
else
{
failureCallback();
}
});
}
function getSettings_success()
{
// Ready to update the ng-repeat NOW.
}
function getSettings_error()
{
alert('Error');
}
$scope.getSettings(1, getSettings_success, getSettings_error);
Now, when i have the $scope.Settings.Sources ready, all I have to do is to print each soruce in specific HTML template. Because of that, I was thinking to use the ng-repeat for this task. Something like this:
<div ng-init="sources=$scope.Settings.Sources">
<ul>
<li ng-repeat="source in sources">
{{ source }}
</li>
</ul>
</div>
Unfortunately, this code isn't working. This because when the page is loading, the $scope.Settings.Sources isn't ready yet. So, the ng-repeat must run after the "sources" page was downloaded.
Is this possible? If yes, how?

You shouldn't be using ng-init for such case, because data is coming by Ajax and you are assigning blank object to source. Cause doesn't responded yet. Rather I'd use Settings.Sources variable directly on your view.
Markup
<div>
<ul>
<li ng-repeat="source in Settings.Sources">
{{ source }}
</li>
</ul>
</div>
Side Note: $scope variable's directly accessible inside angular directives, you don't need explictly specify $scope

Related

View is getting initialized again and again

I'm building a dashboard similar to this one Admin Dashboard Theme
I'm implementing the Right Side SideBar as show in the screenshot
My App code structure is something like:
index.html:
<div ui-view=""></div>
main.html
<div ui-view=""></div>
<div ng-include='"app/dashboard/sidebar.html"'></div>
I have done nesting of view in my main.html where I'm injecting different views. Also, since my right side sidebar is fixed, I want it to be common in all the main views. So, I have just included it in my main.html.
Now, the problem is somehow my sidebar.html is getting initialized again and again no matter if I scroll my page down or perform any action inside sidebar. I have verified it by printing console logs for every controller function which are used in sidebar.html view.
This problem is related to my this post: Earlier, I wasn't able to figure out the actual issue.
Following is my controller and jade code:
angular.module('myApp')
.controller('SidebarCtrl', function($scope, $rootScope) {
$scope.message = {};
$scope.addSideBarToggleClass = function() {
console.log("addSideBarToggleClass");
return true;
}
$scope.getStatusClass = function(status) {
console.log("getStatusClass");
return 'is-online';
}
$scope.openChat = function(receiver) {
console.log("openChat");
}
// etc...
});
<aside ng-class="{ 'control-sidebar-open' : addSideBarToggleClass()}"
ng-controller="SidebarCtrl">
<ul>
<li ng-class="{active: isTabSelected('chat')}">
<a data-toggle="tab" ng-click="updateCurrenTab('chat')"></a>
</li>
<li ng-class="{active: isTabSelected('home')}">
<a data-toggle="tab" ng-click="updateCurrenTab('home')"></a>
</li>
</ul>
<div>
<div ng-class="{active: isTabSelected('home')}">
<h3>Recent Activity</h3>
</div>
<div ng-class="{active: isTabSelected('chat')}">
<div>
<h4>Chat {{noOfUsersOnline}}</h4>
<div>Friends
<a href="#" ng-repeat="user in users" ng-click="openChat(user)">
<span ng-class="getStatusClass(user.status)"></span>
{{user.name}}</a>
</div>
</div>
</div>
</div>
</aside>
I see many logs of "addSideBarToggleClass", "getStatusClass" and every time I click on openChat, I see a log of "openChat" and then again "addSideBarToggleClass" and "getStatusClass"
Can anyone please point out what can be the possible problem for this behavior?
You need to familiarize yourself with the concept of a digest loop in Angular.
In short, every time a digest loop runs, all expressions, e.g. {{name}} or ng-show="isActive && isEnabled", that are being "$watched" by Angular are evaluated (sometimes more than once). This means that if you are invoking a function inside an expression:
<div ng-show="isShown()">
$scope.isShown = function(){
console.log("expect to see this text a lot of times");
return true;
};
the function will be executed on every digest loop.
A digest loop runs any time that something in Angular calls $scope.$digest, which by default happens on things like ng-click or ng-change or $http.then, etc..

(angularJS) make ng-click open a link on an ng-repeat

New to Angular, so sorry if this has been covered somewhere but it's hard for me to explain exactly what I want in succinct terms.
Currently I have an ng-repeat pulling data from a JSON object and making a list.
<li ng-click="openLink()" ng-repeat="location in locations">
{{location.name}}
</li>
Each object has a key called "link" and that key has a property that is a url. I want the function openLink() to open the link associated with each object. I'm not sure how to go about this in the controller.
I know i can do
<li ng-click="openLink()" ng-repeat="location in locations">
<a ng-href="{{location.link}}">{{location.name}}</a>
</li>
but i'd like to be able to do it with ng-click instead to keep the index.html cleaner. So what do I put in here to accomplish the same thing?
$scope.openLink = function() {
};
If the url that you want to redirect to is a route in your angular app, you need to inject the $location service into the controller, then in your function, set the path property on $location.
<li ng-click="openLink(location)" ng-repeat="location in locations">
{{location.name}}
</li>
$scope.openLink = function(location){
$location.path(location.link);
}
If the location link is a complete url, you should inject the $window service into your controller, and change the openLink function to set $window.location.href.
$scope.openLink = function(location){
$window.location.href = location.link;
}

Implementing a page load performance directive in AngularJS

I am very new with AngularJS so I need quite some pointing in the right direction.
The task is to create some kind of widget that displays how much time it takes from any user action until the requested page finishes rendering.
We are going to be using AngularJS at the presentation layer and the back-end will be Microsoft's Web API.
So I figured I could use the browser's Navigation Timing API and wrap it on an AngularJS directive so I tried this:
angular.module('performanceDirective', [])
.directive('pagePerformance', function(){
return {
restrict: 'AE',
replace: 'true',
template: '<div><label id="loadTimeEllapsed">Total Load Time:{{totalLoadTime}}</label></div>',
scope: {},
link: function (scope, elem, attrs) {
scope.$watch('window.performance.timing', function (newValue, oldValue) {
var timing = window.performance.timing;
var userTime = timing.loadEventEnd - timing.navigationStart;
scope.totalLoadTime = userTime;
});
}
};
});
But it seems that there is something missing because even though I am doing actions that call the back-end the number that gets displayed after the home page loads is never updated.
Is this something that actually would work, provided we fix whatever is failing, or is this a dead end and we need to find another option?
UPDATE
The use of the directive has nothing to it, basically it is just the element thrown on a page:
<body ng-app="myApp">
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li>Some Action</li>
</ul>
</div>
</div>
<div class="row">
<div class="span4"><data-page-performance /></div> <!-- The Directive -->
<div class="span10" ng-view></div>
</div>
</body>
Apparently this directive only works if I refresh the page after I have already navigated to it but if I click on an element that will trigger an action on the AngularJS controller the performance number is completely unaffected.

AngularJS - Run directives explicitly

I'm new to AngularJS and I'm struggling with the following issue.
I need to implement a 3 step workflow as follows:
Make a call to a web service that returns a list of strings. For example, ["apple", "banana", "orange"], etc. I intercept the response and add the angle brackets around each of these strings before I send it to the Views.
For each of the string returned by the service, I have to render
<apple />
<banana />
<orange />
Finally, get the actual AngularJS directive corresponding to each of those strings to "execute" (not sure what the right word is) and replace the elements above with the content from the templateUrl property as mentioned in each of their respective directives.
Right now, I'm doing Step 1 and Step 2 above using AngularJS. But I understand that they can be done using plain JavaScript using AJAX calls.
My problem is that the directives don't get "run" or "executed" and I have these tags displayed as plain text on the page -
<apple />
<banana />
<orange />
etc.
How do I tell Angular to replace the custom tags with the actual content from their templates?
Thanks for your help.
UPDATE: Here's what the code looks like:
<div class="content" ng-controller="mainController">
<ul class="feeds">
<li ng-repeat="fruit in fruits">
<div ng-controller="fruitSpecificController"> {{fruit}} </div> <!-- This renders <apple />, <banana />, etc. -->
</li>
</ul>
</div>
Also note that each fruit can have its own controller. In the code above, I say "fruitSpecificController", but ideally that would also be generated at runtime. For example, "appleController", "orangeController", etc. and yes, they'll be child controllers of the parent "mainController".
You can use the compile method, but there is a built in directive that will do this for you - if you are willing to load in via a URL.
ng-include
Using ng-include="'/path/to/template.html'" - the evaluated expression URL will be requested and added to the DOM as a child (compiled for you).
You can also cache the templates using $templateCache (if you want to request multiple templates at the same time or cache it for multiple includes).
That would look something like this:
$templateCache.put(/path/to/template.html, 'apple html string');
custom directive (with $compile)
Otherwise, if you want to load in and compile a string - use a directive inside of a ng-repeat.
.directive('unsafeHtmlCompile', function($compile){
return {
link: function(scope, element, attrs){
scope.$watch(attrs.unsafeHtmlCompile, function(val){
if(val !== undefined){
element.html('');
var el = angular.element(val);
element.append(html);
$compile(el)(scope);
}
});
}
}
}
Remember to remove the watcher, if your data won't change :-)
You probably just need to use the $compile service. The docs aren't super helpful but the gist is that you call $compile, passing in the DOM element (in your case the parent of your directives). That returns a function that you then execute, passing in the scope that you want to use ($rootscope is probably safe).
$compile(element)($rootScope);

knockout js jquery Load

Hi I am trying to load knockout js html pages dynamically from jQuery Load but getting
You cannot apply bindings multiple times to the same element.
How do I load knockout js pages with jqyuery load.
What I am trying is to create a navigation framework by loading url page (Views/home.html or Views/login.html)into when ever user click on link.
So I can't load all knocokout viewmodel on fist load
I am trying to create Navigation model to load/refresh only body of the page not the full page.
For example
<ul class="nav navbar-nav" id="nav">
<li>
Home
</li>
<li>
Login
</li>
<li>
Contactus
</li>
</ul>
<div id="body"></div>
if Home click
$("#body").load("Views/home.html");
if Login click
$("#body").load("Views/login.html");
Home.html
var homeViewModel= function() {
this.firstName = ko.observable();
this.lastName = ko.observable();
};
ko.applyBindings(new homeViewModel());
Login.html
var loginViewModel= function () {
this.username = ko.observable("test");
};
ko.applyBindings(new loginViewModel())
I have attached and removed node but the dependency is always name of viewmodel must be viewModel
ko.cleanNode($("#body")[0]);
$("#body").load(url, function() {
ko.applyBindings(new viewModel(), $("#body")[0]);
});
You may want to look at Pager.js, which extends knockout.js for page history and partial loading.
Load all your javascript in your main page.
Keep the partials (Views/home.html, Views/login.html) containing only html with binding declarations.
You don't need to explicitly call applyBindings when switching partial, Pager.js does that for you (with proper cleanup for all bindings on previous partial).

Categories

Resources