I've been searching around this afternoon trying to figure out how to use Typescript to change the html of a span element in my breadcrumb trail, OR get an angular statement to evaluate/compile in a jQuery selector. The closest I've found is to use $compile, but I'm not sure if this will work since I don't have the data until after my resolve completes? If it will, I'll need a hand understanding where to inject $compile into the app.
index.module.ts
angular.module('pccrm', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngMessages', 'ngAria', 'ngResource',
'ui.router', 'ui.bootstrap', 'feature-flags', 'trNgGrid', 'pascalprecht.translate', 'toastr', 'cgBusy',
'fixtable', 'angularUtils.directives.uiBreadcrumbs', 'LocalStorageModule'])
...
index.route.ts
...
.state(Constants.SEARCH_CONTACTS_CONTACTPROFILE, {
url: '/orgs/:externalOrgId/contacts/:profileId',
data: {
displayName: 'Contact Profile'
},
views: {
'': {
templateUrl: 'app/contacts/contact-profile.tmpl.html',
controller: 'ContactsProfileController',
controllerAs: 'cpc',
resolve: {
contactProfile: function (contactProfileService: IContactProfileService, $stateParams: any) {
return contactProfileService.getContactProfile(<string>$stateParams.externalOrgId, <string>$stateParams.profileId)
.then(function (contactModel: IContact) {
return contactModel;
});
}
}
}
}
})
...
contact-profile.tmpl.html
...
<div class="info-group">
<div class="profileImage default">
</div>
<h4 class="contact-name">{{ cpc.contactProfile.fullName() }}</h4>
{{ cpc.contactProfile.title }}<br/>
<a ui-sref="search.organizations.orgProfile({externalOrgId: cpc.contactProfile.externalOrganizationId })" ng-if="cpc.contactProfile.externalOrganizationId">
{{ cpc.contactProfile.externalOrganizationName }}
</a>
</div>
...
Is something like...
<script type="text/javascript">
$('.breadcrumb li.active span').html(
$compile("{{ cpc.contactProfile.fullName() }}")(scope));
</script>
on the end of the template html the best way to do this? Or can my profile service edit the DOM somehow once it retrieves the contact info?
NOTE We're using angular-util-sui-breadcrumbs to generate the BCT...which currently doesnt support interpolating the resolve into the BCT with the way we have our nested named views. This is why I need to be able to modify the BCT dom after the fact.
Or can my profile service edit the DOM somehow once it retrieves the contact info
Yes. Export the stuff on the scope and use that {{somethingOnScope.contactProfile.fullName()}} in the UI.
More on scope : https://docs.angularjs.org/guide/scope
Update
You may have misread my question
No. I am saying that don't do:
$('.breadcrumb li.active span').html(
$compile("{{ cpc.contactProfile.fullName() }}")(scope));
Instead your html should look like:
<ul class="breadcrumb">
<li class="active">
<span>{{somethingOnScope.contactProfile.fullName()}}</span>
</li>
</ul>
And then you don't need jquery 🌹
Related
I have a simple Angular app (for training purpose since I am a Angular-rookie :)) with a ul-list containing some books. If the user clicks on a book a detail view will appear below the list with some details about that book. I want that if the user clicks on the book the url should change to something like /#/[book-id]. The user should also of course be able to browse directly to this url where the details view for the specific book is visible. I have not figure out how to do this with routes. I paste my sample code below:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body ng-app="libraryApp">
<div id="list" ng-controller="listCtrl">
<ul ng-repeat="book in data.list">
<li>{{ book.name }}</li>
</ul>
</div>
<div id="detailView" ng-controller="detailCtrl">
<div ng-if="data.currentBook" style="background-color: #EEE; padding: 10px 0;">
<h1>{{ data.currentBook.name }}</h1>
<div>Number of pages: {{ data.currentBook.pages }}</div>
</div>
</div>
</body>
</html>
<script>
angular.module("libraryApp", [])
.factory("dataFactory", function() {
return {
list: [
{ id: 1, name: "The Great Gatsy", pages: 423 },
{ id: 2, name: "1984", pages: 332 },
{ id: 3, name: "The Lord Of The Rings", pages: 632 }
],
currentBook: null
};
})
.controller("listCtrl", function($scope, dataFactory) {
$scope.data = dataFactory;
$scope.openDetailView = function(book) {
$scope.data.currentBook = book;
};
})
.controller("detailCtrl", function($scope, dataFactory) {
$scope.data = dataFactory;
});
</script>
you have to put this code in your code ...
angular.module("libraryApp", ['ngRought'])
.config(function ($routeProvider)
{
$routeProvider
.when("/bookname",
{
templateUrl: "path of the html file",
controller: "listCtrl"
})
.when("/date",
{
templateUrl: "path of the html file",
controller: "detailCtrl"
})
Kinda difficult to answer since there are so many ways to do this, but look into https://github.com/angular-ui/ui-router
You'll use your module's config block to set up the routing with $stateProvider, then specify the controllers and templates you'd like to use when a user changes the application's state. This means you'll probably need to move those divs for your different views to separate files (or something creative to get their content -- getting the element's html with jQuery or something would work).
I'm not going into many details because this is something that can be answered easily now that you know to use ui-router :)
It's called nested view. To use it you'd be better try ui-router: https://angular-ui.github.io/ui-router/sample/#/contacts/1/item/b
I am having a few issues getting my head around why this is not working:
template
<div>
<ul>
<li ng-repeat="x in get">
{{ x.name }}
</li>
</ul>
</div>
controller
angular.module('app')
.controller('SubstancesCtrl', function ($scope) {
$scope.get = [{name: "LOL"}];
});
routes
angular.module('app')
.config(function ($routeProvider) {
$routeProvider
.when('/', { controller: 'PostsCtrl', templateUrl: '/templates/posts.html' })
.when('/register', { controller: 'RegisterCtrl', templateUrl: '/templates/register.html' })
.when('/login', { controller: 'LoginCtrl', templateUrl: '/templates/login.html' })
.when('/substances', { controller: 'SubstancesCtrl', templateUrl: '/templates/substances.html'})
});
index.html
<!DOCTYPE html>
<html ng-app='app'>
<head>
<link rel='stylesheet' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'>
<link rel='stylesheet' href='/app.css'>
</head>
<body ng-controller='ApplicationCtrl'>
<nav class='navbar navbar-default'>
<div class='container'>
<ul class='nav navbar-nav'>
<li><a href='/#/'>Posts</a></li>
<li><a href='/#/register'>Register</a></li>
<li><a href='/#/login'>Login</a></li>
<li>Substances</li>
</ul>
<p ng-if='currentUser' class='navbar-text navbar-right'>
Signed in as {{currentUser.username}}
</p>
</div>
</nav>
<div class='container' ng-view></div>
<script src='http://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.4/angular.js'></script>
<script src='http://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.4/angular-route.js'></script>
<script src='/app.js'></script>
</body>
</html>
I can see that the template is being replaced by the ng-view directive however the get variable is not being treated in the manner i thought it would: i thought i would get one list item node with the value of LOL
You angular module has not been created, You should declare your module in app.js. As you are using angular routing you need to include its module in your app module.
angular.module('app',['ngRoute'])
Also you need to update your Angular to stable versions 1.4.4 is stable one, because you are using 1.3.0-rc.4 in which rc stands for release candidate which is pre-release version, which may have bug in most of the cases. Thats why I told you to go for stable version of AngularJS. You could also use close stable version which is 1.3.14
Additionally your template has <li ng-repeat="x in get()"> but get function is not defined in the scope, resultant would be nothing would be render on the UI.
angular.module('app')
.controller('SubstancesCtrl', function ($scope) {
$scope.lol = "LOL";
$scope.get = function(){
//write a code here which will return list
//so that ng-repeat will loop through the list
//& will show that data on HTML
//something like below
return {"name": "LOL"}; //in real case there must be something different object to return.
}
});
Alexis King was quite correct in that there were 3 very embarrassing issues with the code, it should have looked like this ( -_- )
template
<div>
<ul>
<li ng-repeat="x in get">
{{ x.name }}
</li>
</ul>
</div>
controller
angular.module('app')
.controller('SubstancesCtrl', function ($scope) {
$scope.get = [{name: "LOL"}];
});
routes were exactly the same.
The biggest issue however was in my gulp script which failed to pick up changes in these new files, I have rewrote the build script to ensure all files from the directories containing these files now are watched and all changes are reflected in the browser.
My excuse: need more coffee.
Why doesn't Angular interpolate values in data attributes?
eg:
this template:
<img data-src="https://farm4.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" src="https://farm4.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" title="{{photo.title}}" />
renders this:
<img data-src="https://farm4.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" src="https://farm4.staticflickr.com/3861/14472009669_e97c9a201a.jpg" title="DSC00319">
Everything is interpolated but the data attribute. I've seen a few other questions on this but not a good explanation as to why this behavior and what I ought to be doing instead.
I'm loading this route's module with the config below:
.config(['$stateProvider', function config( $stateProvider ) {
$stateProvider.state( 'all', {
url: '/all',
templateUrl: 'src/app/all/all.tmpl.html',
controller: 'all'
});
}])
// Sample controller where service is being used
.controller('all', ['$scope', '$http', 'version', function ($scope, $http, version) {
$scope.scopedAppVersion = version;
$http.jsonp('https://api.flickr.com/services/rest/?method=flickr.people.getPublicPhotos&api_key=blahblah&user_id=57933175#N08&format=json&per_page=50&page=1&jsoncallback=JSON_CALLBACK')
.success(function(data){
$scope.photos = data.photos.photo;
});
}]);
all.tmpl.html
<li ng-repeat="photo in photos">
<a href="https://www.flickr.com/photos/neptunian/{{photo.id}}/in/photostream/lightbox/" target="new">
<img data-src="https://farm4.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" src="https://farm4.staticflickr.com/{{photo.server}}/{{photo.id}}_{{photo.secret}}.jpg" title="{{photo.title}}" />
</a>
</li>
UPDATE
I think that 'data-src' is perhaps a reserved attribute for Angular. Using 'data-blahblah' or any other attribute works fine.
I tried on jsfiddle, and there is a little mistake.
Use the data-ng-src instead of data-src. The directive is called data-ng-src or just ng-src.
<img data-ng-src="{{'http://www.w3schools.com/images/'+photo.img || ''}}" title="{{photo.title}}" />
or
<img data-ng-src="http://www.w3schools.com/images/{{photo.img}}" title="{{photo.title}}" />
Attribute differences:
data-src: one of the data-* attributes that can be added to an element
src: the usual for the url of img elements.
data-ng-src or ng-src: angular evaluates the value and sets the src to the img element.
I am having a very troubling problem where ng-click updates the expected scope variables, the scope variables appear to change on the DOM ( when viewed via the chrome debugger) but the dom elements that have changed are not redrawn in the browser. I have noticed across multiple browsers and devices BUT ONLY WHEN THE SCREEN WIDTH IS BELOW 768. I am using angular version 1.2.6. Has anyone come across this type of issue??
I have simplified the code to try to isolate and confirm what is happening. Here is the code in its simplified version:
First the view:
<section class="stream-area">
<div class="group">
<div class="gw-fixed-top-panel">
<div class="group-heading ">
<div class="panel-title">
<span class="fleft name" ng-click="usergroup.collapsed=!usergroup.collapsed"> CLICK HERE </span>
<a ng-show="usergroup.collapsed" title="Click to edit group" ><i class="fa fa-cog"></i></a>
<a ng-hide="usergroup.collapsed" title="Click to edit group" ><i class="fa fa-chevron-up"></i></a>
<div> {{usergroup.collapsed}}</div>
</div>
</div>
</div>
</div>
</section>
The controller does nothing at this point...
'use strict';
(function () {
var groupController = function($scope) {
$scope.usergroup={};
$scope.usergroup.collapsed=true;
};
myOverall.myApp.controller('GroupController',['$scope', groupController]);
}());
The controller is invoked by via .config:
(function () {
golfWire.gwWebApp =angular.module('gwWebApp', [
'ngRoute',
'ngAnimate',
'ngResource',
'ui.bootstrap',
'firebase',
'LocalStorageModule',
'ngGrid'
]);
myOverall.myApp
.config(['$routeProvider','$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when('/group/view', {
controller: 'GroupController',
templateUrl: '/app/views/group.html'
})
.otherwise({
redirectTo: '/main'
});
/* $locationProvider.html5Mode(true); */
}]);
Add this function in your controller
$scope.toggleUserGroup = function(){
$scope.usergroup.collapsed = ! $scope.usergroup.collapsed;
$scope.$apply();
}
Call this function on ng-click. This should get the DOM updated and refreshed.
Reasoning: By calling $apply() you trigger a digest() cycle, which checks for changed expression and repaints the DOM if need be.
Incase this doesn't fix the issue, please provide a plunkr/fiddle. Would be happy to get some working code.
Thanks,
A slight variation on Eliel's answer in AngularJS - bind to directive resize worked for me. In the directive.js:
$scope.onResizeFunction = function() {
};
// Call to the function when the page is first loaded
$scope.onResizeFunction();
angular.element($(window)).bind('resize', function() {
$scope.onResizeFunction();
$scope.$apply();
});
I call
$(window).resize();
from within my app.js. The directive's d3 chart now resizes to fill the container.
I have an app that behaves normally when I follow links on the page, but if I use the browser's back and forward buttons, it breaks in the following ways:
It sends a request to the server, but only on "Back".
The template is not rendered at all.
In addition, when I hit "back" to go from page B to page A, chrome's "refresh/stop" button flickers between the top options rapidly, and repeated attemptes to go back and forward causes longer flickering.
Here are the code snippets that I think are relevant:
Edit: I'm working on a plnkr but the site is currently not working. I'll update when it's up and I can verify the bad behavior
Edit 2: Here is the plnkr, but it has problems. It can't find the templateUrls specified in app.js routing, not sure why. Here's the code anyway http://plnkr.co/edit/6cQtnvLi10sJKW8jVzVM
Edit 3: With the help of a friend, I think the problem is coming from using turbo-links on rails 4. I can't test it right now, but when I can I'll post an answer if it works.
file: app.js
window.App = angular.module('app', [
'templates',
'ui.bootstrap'
])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/', {
controller: 'HomeCtrl',
templateUrl: 'home.html'
})
.when('/bars', {
controller: 'BarsPublicCtrl',
templateUrl: 'bars_public.html'
})
.when('/bars/:bar_name', {
controller: 'BarsDetailPublicCtrl',
templateUrl: 'bars_detail_public.html'
})
.otherwise({redirectTo: '/'});
}]);
file: bars_public_ctrl.js
App.controller('BarsPublicCtrl', ['$scope', '$location', 'BarsPublicDataFactory',
function($scope, $location, BarsPublicDataFactory) {
// memoization
$scope.bars = $scope.bars || BarsPublicDataFactory.getBars();
}
]);
BarsPublicDataFactory just returns a static array of fake data, same with the factory in the following snippet
file: bars_detail_public_ctrl.js
App.controller('BarsDetailPublicCtrl', ['$scope', '$routeParams', 'BarsDetailPublicDataFactory',
function($scope, $routeParams, BarsDetailPublicDataFactory) {
$scope.bar = {};
$scope.bar.name = $routeParams.barId;
$scope.barDetails = BarsDetailPublicDataFactory.getBaz($routeParams.bar_name);
$scope.Bazs = BarsDetailPublicDataFactory.getBazs();
}]);
file: bars_public.html
<div class="container">
<div ng-repeat="bar in bars">
<div class="row">
<div class="col-sm-12">
<a href="/bars/{{bar.name}}">
<h4 style="display:inline;">{{ bar.name }}</h4>
</a>
</div>
</div>
<hr />
</div>
</div>
file: bars_detail_public.html
<select ng-model="searchSelect.style" style="width:100%;">
<option value='' selected>All</option>
<option ng-repeat="baz in bazs">{{baz}}</option>
</select>
<div>
<accordion close-others="true">
<accordion-group ng-repeat="foo in foos | filter:searchSelect">
<accordion-heading>
<div>
<h3>{{foo.name}}</h3>
<em>{{foo.style}}</em>
</div>
</accordion-heading>
</accordion-group>
</accordion>
</div>
If you need anything else, let me know.
It turns out that the problem was caused by turbolinks with Rails 4. I failed to mention it because I didn't realize it was important.
I don't know exactly what caused it, but turbolinks injects some javascript, and as best as I can tell, it highjacks some events that cause the page to reload when you use the browse buttons which was breaking my app.
So I followed this advice: http://blog.steveklabnik.com/posts/2013-06-25-removing-turbolinks-from-rails-4
and it worked just fine! Hope someone else can benefit.
befoure:
[installed]jquery_turbo_links
[installed]turbolinks
alter:
[installed]jquery_turbo_links
[removed]turbolinks
It worked!