AngularJS not interpolating values in data attributes - angularjs

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.

Related

Angular: Why Won't Model Update Outside Of View In Same Controller?

I have found a number of posts talking about models/views not updating, but I still can't figure this out. I'm new to Angular, btw so I suspect this is noob issue.
I have the below Angular app.
When the text input test_var is edited, the edited value isn't updated in the sidebar, but it is in the view. Why and how do I fix it?
This works when I don't use views and routes.
I've tried a sidebar controller, but no difference. I've tried $rootScope, which partially worked (it broke other functionality), but I'd prefer not to use a global scope.
Thanks for taking a look.
HTML
<body>
<div ng-app="rxApp" ng-controller="WizardCtrl">
<div class="ng-view">
</div>
<div class="sidebar">
<span ng-bind="test_var"></span>
</div>
</div>
</body>
View (One.html)
<input ng-model="test_var" />
<span ng-bind="test_var"></span>
Controller
rxApp.controller( 'WizardCtrl', function ( $scope, $http, $routeParams, $location, FileUploader ) {
$scope.test_var = 'please work!';
)};
Routes
rxApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/one', {
templateUrl: 'templates/one.html',
controller: 'WizardCtrl'
}).
when('/two', {
templateUrl: 'templates/two.html',
controller: 'WizardCtrl'
}).
otherwise({
redirectTo: '/one'
});
}
]);
Controllers are disposed when transmuting routes.
As for Best Practice you should create a Service to handle that data. You should not use controllers to carry data between views. In your case, the problem is Controllers are disposed when transmuting routes.
See the angular docs on how to use controllers correctly. http://docs.angularjs.org/guide/dev_guide.mvc.understanding_controller
The problem is the making of model variables.
You have to have a dot in model. Make your model point to an object.
So in your controller do like this -
rxApp.controller('WizardCtrl',function($scope, $http, $routeParams,$location, FileUploader){
$scope.test ={
var : 'please work!'
};
)};
View (One.html)
<input ng-model="test.var" />
<span ng-bind="test.var"></span>
HTML
<body>
<div ng-app="rxApp" ng-controller="WizardCtrl">
<div class="ng-view">
</div>
<div class="sidebar">
<span ng-bind="test.var"></span>
</div>
</div>
</body>
To read more about scope go through this UnderStanding Scopes

Typescript - change DOM element inner HTML?

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 🌹

Angular UI-Router Help - Show Elements Based On State

First off I am new to angular and I am inheriting this project. This seems like it should be deathly easy to say if the state is this show this, but I keep failing :]
angular.module('myApp')
.config(function ($stateProvider) {
$stateProvider
.state('page1', {
url: '/page1',
data: etc..................
},
.state('page1.nest', {
url: '/page1/nest',
data: etc..................
},
.state('page2', {
url: '/page2',
data: etc..................
},
Each page has it's own controller. page1 and page1 nest share the same templateUrl: 'scripts/app/page.html'. The div below lives on the page.html.
How do I simple say...
<div>
<span ng-show="if state == page1">Page 1</span>
<span ng-show="if state == page1/nest">Page 1 Nest</span>
</div>
<div>
Same Content on both here
</div>
I think it has something to do with $stateParams needing to be exposed to the scope in the controller?
You'll want to use the $state service provided by UI-Router
Check the documentation out here.
You'll do something like
In your controller. Inject $state then set it to a scope variable
$scope.state = $state
Then you can do something like this.
<span ng-show="state.is('page1')">Page 1</span>
Alternatively obviously you can use a function in your controller.
<span ng-if="inState('page1')">Page 1</span>
In controller
$scope.inState = function(state){
return $state.is(state);
}
Let's start first by formatting your code a little better and closing parens where they should be closed.
angular.module('myApp')
.config(function($stateProvider) {
$stateProvider
.state('page1', {
url: '/page1',
template: 'templates/page1.html',
data: etc..................
}),
.state('page1.nest', {
url: '/page1/nest',
template: 'templates/page1.nest.html',
data: etc..................
}),
.state('page2', {
url: '/page2',
template: 'templates/page2.html',
data: etc..................
});
Typically you would have these various templates somewhere in your project structure like
/app
/templates
Then you have your index page...
<html ng-app>
<body>
<div ng-controller="myController">
<div ui-view></div> <!-- this is where the page1 or page2 state gets loaded -->
</div>
</body>
</html>
Then in your page1 or page2 html files
<div>
... content goes here ...
<div ui-view></div> <!-- nested content will go in here -->
</div>
Then in your page1.nest.html
<div>
... all your nested content goes here
</div>

ui-router rendering ui-views in view templates

Just checking an approach for ui-router. Was sure it could do this but hitting some friction. I want my third template to render inside my 2nd template - but my controller is not even initialized for my 3rd state unless I define my ui-view in my 1st template.
Example code
Template 1 This is rendered from an MVC view
<div class ="animate-container" ng-app="uiRouter-Browse">
<div class="products-slide-animate" ui-view="cat1">
<div>
<div><a ui-sref="cat1({id:1})">1</a></div>
<div><a ui-sref="cat1({id:2})">2</a></div>
<div><a ui-sref="cat1({id:3})">3</a><div>
</div>
</div>
</div>
Template 2
<div>
<div class="row all-categories-wrapper">
<div class="col-xs-12 list-item">
<a href="#/" class="parent">
<i class="chevron-left"></i>
<div>All Categories </div>
</a>
</div>
</div>
</div>
<!--want 3rd template to render here -->
<div ui-view="cat2" class="products-slide-animate" autoscroll="false">
<div class="cat2-wrapper" ng-repeat="cat1 in data.Cat1s">
<div class="row">
<div class="col-xs-12 list-item">
<a ui-sref="cat2({id:{{cat1.ID}}, name:'{{cat1.UrlEncodedName}}'})">
<div class="list-item-text">{{cat1.Name}}</div>
<i class="chevron-right"></i>
</a>
</div>
</div>
</div>
</div>
Template 3
<div>
//some content to render
</div>
My ui-router script
var browse = angular.module('uiRouter-Browse', ['ui.router', 'ngAnimate'])
.run(
['$rootScope', '$state', '$stateParams',
$rootScope.$on('$stateChangeSuccess',
function (event, toState, toParams, fromState, fromParams) {
console.log(toState.name);
console.log(fromState.name);
}
);
}]);
browse.config(
['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider
.otherwise('/');
$stateProvider
.state('cat1', {
url: '/cat1/:id?name',
views: {
'cat1': {
templateUrl: '/Template1.html',
controller: //Get some data and return
}
}})
.state('cat2', {
url: '/cat2/:id?name',
views: {
'cat2': {
templateUrl: '/Template2.html',
controller: //Get some data and return
}
}
});
}]);
Most if not all of the examples I see the entire ui-view is replaced, as opposed to partially being replaced - i.e. the rendering of template 3 in the ui-view in template 2.
So when click on the cat 1 links from template 1, it transitions to the next state, the controller is invoked and my animations are pretty.
When I click on a cat2 link, my state is invoked correctly but the controller is not fired. I then just animate back to my previous view/state.
If I place a ui-view="cat2" div in template 1 then the controller fires and my template renders. I just would like it to render within the ui-view in template2.
Thanks
While not sure if it will really suite to your needs, the reason and solution is in a different state definition, we need state nesting. We simply cannot have two "totally" independent states, and try to inject one into another. We have to make one of them Parent and one to be a Child:
Current scenario:
$stateProvider
.state('cat1', {
...
}})
.state('cat2', {
...
});
Both states are in this snippet on the same "root" level. If we would like to nest the cat2 into cat1 they must be defined like this:
$stateProvider
.state('cat1', {
... // this is a parent
}})
.state('cat1.cat2', {
... // this is a child
});
That will lead to url for a (sub)state cat2 to be built from both parent and its own:
#/'/cat1/:id/cat2/:id?name&nameurl',
but if we do not need Parent url part and its parameters we can use absolute url:
.state('cat2', {
url: '^/cat2/:id?name',
See:
Methods for Nesting States
Absolute Routes (^)

nest ng-view inside a form

given the controller
function ctl($scope, $http) {
$scope.postForm = function() {
console.log("submitting form")
}
}
and the view
<form name="pform" ng-show="!error.Show">
<div ng-view></div>
<button type='button' value='Save' ng-click="postForm()" />
</form>
The controller method postForm doesn't get called, however, if i move the form tag into the view the method is called. Is there a reason that this doesn't work as I expect it to? Is there another way to accomplish the goal of sharing the form controls across different views?
Update
my module and routeProvider are configured like this:
angular.module("profilemodule", [])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when("/info", { templateUrl: '/partials/profile/info.html', controller: ProfileController })
.when("/user", { templateUrl: '/partials/profile/user.html', controller: ProfileController })
.when("/introduction", { templateUrl: '/partials/profile/editor.html', controller: ProfileController })
.otherwise({ redirectTo: '/info' });
}]).run(function ($rootScope, $location) {
$rootScope.location = $location;
})
and the page includes some nav elements which are set based on the location service like so:
<div class="row">
<div class="offset2 span10">
<ul class="nav nav-pills">
<li ng-class="{active: location.$$path.substring(0, '/info'.length) == '/info'}"><a href="#/info" >Information</a></li>
<li ng-class="{active: location.$$path.substring(0, '/user'.length) == '/user'}"><a href="#/user" >User</a></li>
<li ng-class="{active: location.$$path.substring(0, '/intro'.length) == '/intro'}"><a href="#/intro" >Introduction</a></li>
</ul>
</div>
</div>
<form name="pform" method="POST" ng-show="!error.Show">
<div ng-view></div>
<button type='button' value='Save' ng-click="postForm()" />
</form>
the ng-class statements works perfectly, is it because I've set the location property of $scope in the module's run method?
thanks,
jason
ng-view with routing creates a new scope with the controller, and you can't reach a child scope. Your submit action lies in the parent scope and the form data lies in the child scope (created by ng-view).
If you want to use common form controls, you can use ng-include, this directive gets template it and renders that in the current scope.
Move your form controls to a new template, then include them in all of your forms.
API reference:
http://docs.angularjs.org/api/ng.directive:ngInclude

Resources