How to manipulate the <title> of an AngularJS single page app? - javascript

I have a single-page AngularJS app.
The index.html file looks like this:
<html ng-app="myApp" ng-controller="MyCtrl as myctrl">
<head>
<link rel="stylesheet" href="my-style-sheet.css">
<title>{{myctrl.title}}</title>
</head>
<body>
<div class="container">
<ol>
<li><a ui-sref="stateA">StateA</a></li>
<li><a ui-sref="stateB">StateB</a></li>
</ol>
<div ui-view></div>
</div>
<script src="my-app.js"></script>
</body>
</html>
As the user clicks on the StateA or StateB links, the page displays the content of those pages in place of <div ui-view></div>. Terrific.
As the user clicks around, the displayed content changes. I need the title of the page to change too. Currently it gets the title from the controller MyCtrl like this: <title>{{myctrl.title}}</title>. But when the user clicks those links, those states each have their own controllers. So I cannot use <title>{{myctrl.title}}</title>.
How can I update the title when various states the user clicks on have different controllers?

You can attach some data to each state of your routes, like a value that can be used to set the title of the page.
.state('test', {
url: '/test',
templateUrl: '/templates/test.html',
data: {
title: 'test title'
}
})
Then write a directive to read the title. You can check to see if the required data is available on the state you are going to, and attach the whole thing to $stateChangeSuccess event.
function dynamicTitle($rootScope, $timeout) {
return {
link: function(scope, el) {
$rootScope.$on('$stateChangeSuccess', function(event, toState) {
var title = (toState.data && toState.data.title)
? toState.data.title
: 'Default title';
$timeout(function() {
el.text(title);
}, 0, false);
};);
}
};
}
angular.module('myApp').directive('dynamicTitle', dynamicTitle);
And attach it to your <title>
<title dynamic-title></title>

You can create an AngularJS factory, inject it, modify it by calling 'Title.setTitle()' from controllers
<html ng-app="app" ng-controller="Ctrl">
<head>
<title>{{ Title.title() }}</title>
app.factory('Title', function() {
var title = 'Hello';
return {
title: function() { return title; },
setTitle: function(newTitle) { title = newTitle }
};
});

Related

Isolated Scope not working

Here is my custom directive code
(function () {
var app = angular.module('CustDirMod', []);
var custdirCtrl = function ($scope) {
$scope.Person = {
Name: 'Jagan868',
address: {
street: '10 Donwstreet',
city: 'North Avenue',
state: 'Los Angeles'
},
friends: [
'Friend1',
'Friend2',
'Friend3'
]
};
};
var custDirectivewithBinding = function () {
return {
templateUrl: "Friends.html",
restrict: "E",
controller: function ($scope) {
$scope.KnightMe = function (Person) {
Person.rank = "Knight";
}
}
}
};
app.controller('CustDirCtrl', custdirCtrl);
app.directive("custDirectiveBinding", custDirectivewithBinding);
})();
and here is my template html
<div class="panel panel-primary">
<div class="panel-heading">
{{ Person.Name }}
</div>
<div class="panel-body">
<div ng-show='!!Person.address'>
<h4>Address :
</h4>
{{Person.address.street}}
<br />
{{Person.address.city}}
<br />
{{Person.address.state}}
</div>
<h4>Friends :</h4>
<br />
<ul>
<li ng-repeat='friend in Person.friends'>
{{friend}}
</li>
</ul>
<div ng-show="!!Person.rank">
Rank : {{Person.rank}}
</div>
<button ng-show="!Person.rank" class="btn btn-success" ng-click="KnightMe(Person)">Knight Me</button>
</div>
</div>
Now the following final html page where i'm using the above custom directive
<!DOCTYPE html>
<html ng-app="CustDirMod">
<head>
<title>Simple Directives - Angularjs</title>
<script src="Scripts/jquery-3.1.1.js"></script>
<link href="Content/bootstrap.css" rel="stylesheet" />
<script src="Scripts/bootstrap.js"></script>
<script src="Scripts/angular-1.5.8.js"></script>
<script src="Scripts/CustomDirective.js"></script>
</head>
<body ng-controller="CustDirCtrl" class="container" style="padding-top:30px;">
<cust-directive-binding></cust-directive-binding><br /><br />
</body>
</html>
Now i tried to add isolated scope in my custom directive as follows
var custDirectivewithBinding = function () {
return {
templateUrl : "Friends.html",
restrict: "E",
scope: {
userdata: "="
},
controller: function($scope){
$scope.KnightMe = function (Person) {
Person.rank = "Knight";
}
}
}
};
and then in the html page as follows
<body ng-controller="CustDirCtrl" class="container" style="padding-top:30px;">
<cust-directive-binding userdata="Person"></cust-directive-binding><br /><br />
</body>
After adding the isolated scope named as 'userdata' i'm not getting any data in UI. But if i remove that 'userdata' isolated scope from both js & html file its working fine. How to resolve this issue.
P.S: I don't want name the isolated scope local property name same as "Person". I just want it to be something different so that i can distinguish easily.
You don't have a scope property Person in the directive , you renamed it to userdata when you created the isolated scope.
You either need to change the template to now use userdata instead of Person or change the name of userdata to Person so the template will work
scope: {
userdata: "="
}
// in view
{{ userdata.Name}}
Or
<cust-directive-binding Person="Person">
scope: {
Person: "="
}
// in view
{{ Person.Name}}
Because now inside your directive template data will be available with isolated scope variable userdata. To fix the issue you could use userdata instead of Person every where on template. But instead of doing that I'd suggest you to use alias on isolated scope like Person: "=userdata". Where it says userdata will be attribute inside directive data will be available with Person name.
scope: {
Person: "=userdata"
},

Angular nested controller initialization

I have simple angular app with a Main Controller and a child controller. Maincontroller loads the login page and on successfull login, a welcome page is loaded in using the child controller. The app is simple one and is working. My issue is to set the logged in user name after successfull login.
.controller('mainController', function($rootScope, $location, Auth) {
var vm = this;
vm.loggedinUser = Auth.getActiveUser();
console.log(">>>>>> ", vm.loggedinUser);
//check to see user is logged in for every request made
$rootScope.$on('$routeChangeStart', function() {
vm.loggedIn = Auth.isLoggedIn();
});
My html page is simple:
<body ng-app="bookapp" ng-controller="mainController as main">
..
<li class="navbar-text">Hello {{ main.loggedinUser }}! >
Issue is that loggedinUser value is not displayed.
1) For it to work i have to initialize the loggedinUser value within the routechange listener.(moving the line to within listener works). Why is it so?
2) Also note that the console.log statement prints the real value after log in and when the welcome page is displayed. But variable value is set in main Controller during the application load itself(login page) and can only be changed through route change listener. It is not re-initialized during welcome page load.
You can either pass an object reference to the controller or use the $watch service to evaluate the the getActiveUser function every digest and update the controller scope.
angular.module("bookapp", []);
angular.module("bookapp").controller("mainCtrl", function(Auth) {
var vm = this;
vm.userData = Auth.data;
vm.login = function() {
Auth.data.loginTimeStamp = new Date();
Auth.data.user = "TestUser";
};
});
angular.module("bookapp").factory("Auth", function() {
var data = {
loginTimeStamp: null,
user: null,
};
return {
data: data
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<body ng-app="bookapp">
<div ng-controller="mainCtrl as main">
<p>Hello {{ main.userData.user }}!</p>
<button ng-click="main.login()">Log In</button>
</div>
</body>
angular.module("bookapp", []);
angular.module("bookapp").controller("mainCtrl", function($scope, Auth) {
var vm = this;
$scope.getUser = Auth.getActiveUser;
vm.login = function() {
Auth.setActiveUser("TestUser");
debugger;
};
$scope.$watch(function(scope) {
return scope.getUser();
}, function(newValue) {
vm.loggedinUser = newValue;
debugger;
});
});
angular.module("bookapp").factory("Auth", function() {
var data = {
loginTimeStamp: null,
user: null,
};
return {
getActiveUser: function() {
return data.user;
},
setActiveUser: function(userName) {
data.user = userName;
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<body ng-app="bookapp">
<div ng-controller="mainCtrl as main">
<p>Hello {{ main.loggedinUser }}!</p>
<button ng-click="main.login()">Log In</button>
</div>
</body>

Configure the service angular-lazy-img with a dynamic scrollable container generated on controller

As a new developer on angularjs I have a specific question regarding provider. I'm trying to use the lazy load angular-lazy-load that has a specific functionality regarding the scrollable container. This scrollable container should be set in the config step.
Here is an example :
var myApp = angular.module('myApp', ['angularLazyImg']);
myApp.config(['lazyImgConfigProvider',
function(lazyImgConfigProvider) {
var scrollable = document.querySelector('.container');
lazyImgConfigProvider.setOptions({
offset: 1, // how early you want to load image (default = 100)
errorClass: 'error', // in case of loading image failure what class should be added (default = null)
successClass: null, // in case of loading image success what class should be added (default = null)
onError: function(image) {}, // function fired on loading error
onSuccess: function(image) {}, // function fired on loading success
container: angular.element(scrollable)
});
}
]);
myApp.controller('lazyCtrl', ['$scope',
function($scope) {
$scope.images = [{
url: "https://cdn.sstatic.net/stackoverflow/img/apple-touch-icon#2.png?v=73d79a89bded&a"
}, {
url: "http://www.skrenta.com/images/stackoverflow.jpg"
}, {
url: "http://sourcingrecruitment.info/wp-content/uploads/2015/05/stackoverflow.png"
}, {
url: "http://gillespaquette.ca/images/stack-icon.png"
}, {
url: "http://www.iconsdb.com/icons/preview/black/stackoverflow-3-xxl.png"
}, {
url: "https://avatars0.githubusercontent.com/u/139426?v=3&s=400"
}, {
url: "http://gillespaquette.ca/images/stack-icon.png"
} ];
}
]);
.container {
overflow: scroll;
height: 200px;
position: absolute;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-guide-concepts-1-production</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/Pentiado/angular-lazy-img/master/release/angular-lazy-img.js"></script>
</head>
<body ng-app="myApp">
Test lazy image
<div ng-controller="lazyCtrl">
<div class="container">
<div ng-repeat="image in images">
<img width="100px" height="100px" lazy-img="{{image.url}}" />
</div>
</div>
</div>
</body>
But my question is how can I do to add a container option if the container element is not yet created and will be by the controller ?
Thanks in advance, please let me know which informations are missing to answer.
Ok so the trick is to add an attribut lazyImgContainer to the container in the html file. Not yet documented in github so I have added a pull request to get it documented.

Selection in first drop down populates data in second dropdown but only on first click, why?

I have 2 drop downs where selection of first drop down effects the data content of the second drop down.
The problem I have is this functionality is occurring only on first click instead it should happen every time.
Here is my jsFiddle with my working code and here is my code on jsbin(jsbin gives some error which I am unable to understand, help appreciated here too, #newbieTorture).
Thanks in Advance :)
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.ArticleAdapter= DS.FixtureAdapter.extend({});
App.Article =DS.Model.extend({
title: DS.attr(),
body: DS.attr(),
shouldReloadAll:true,
comments: DS.hasMany('comment', {async : true})
//async tells compiler to load data from comment everytime this is rendered
});
App.Comment =DS.Model.extend({
text: DS.attr(),
shouldReloadAll:true,
article: DS.belongsTo('article', { async: true })
});
App.Article.FIXTURES=[
{
id:1,
title : 'Ember',
body:'Its a great technology but need lot of studying and practice',
comments:[1]
},{
id:2,
title : 'Angular',
body:'it takes less understanding but has more coding the ember',
comments:[2,3]
//this will be an aray coz it is has many relation
}
];
App.Comment.FIXTURES=[
{
id:1,
text : 'Yyyieee excited to learn ember',
aricle: 1
//its not an array coz it will be related to single object
},{
id:2,
text : 'I will start Angular once i have fininshed with ember',
article: 2
},{
id:3,
text : 'Angular can be interesting',
article: 2
}
];
App.CommentAdapter= DS.FixtureAdapter.extend();
App.IndexController = Ember.ArrayController.extend({
articleValue: null,
selected: null,
articleStore: Em.computed(function(){
console.log("data is : " + this.get('articleValue'));
console.log("data is : " + this.get('selected'));
return this.store.findAll("article");
}).property("selected"),
availableComment: Em.computed(function () {
var make = this.get('selected');
// the line below returns the id and not an object
console.log(make);
if (make === undefined || make === null)
return [];
return this.get('selected').get('comments');
}).property('articleValue'),
actions:{
callIndexController: function(){
var select= this.get("selected");
console.log("hi :" + select);
}
}
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [];
},
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
</head>
<body>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
First drop down:
{{
view "select"
contentBinding=articleStore
optionLabelPath="content.title"
optionValuePath="content.id"
prompt="Pick a person:"
shouldReloadAll=true
selectionBinding=selected
valueBinding=articleValue
}}
<br>
<br>
Second drop down:
{{
view "select"
contentBinding=availableComment
optionLabelPath="content.text"
optionValuePath="content.id"
prompt="related task:"
shouldReloadAll=true
valueBinding=articleValue
}}
</script>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.7/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.7/ember.debug.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.7/ember-data.js"></script>
<script src="js/amit_dropdown.js"></script>
<!-- to activate the test runner, add the "?test" query string parameter -->
<script src="tests/runner.js"></script>
</body>
</html>

use AngularJs NgResource to load JSON file from localhost

Overview
I am building an app (running on MAMP) that holds contact information that will expand to hold more data such as project name & deadline, once this part is functional.
Questions
When the user visits /projects.php#/project/ I would like them to see a list of all the project names with a link to their detail page.
How should I write the following to access all of my data?
Do I need the .json at the end?
What does the #id do?
return $resource('data/project.json/:id', {id: '#id'});
When the user visits /projects.php#/project/a-gran-goodn I would like them to see the details about this project(for now, just the name & address).
How should I write the following to return my data by Id?
$scope.project = $routeParams.id ? Project.get({id: $routeParams.id}): new Project();
plunkr file
http://plnkr.co/edit/7YPBog
project.json
This file lives on http://localhost:8888/angularjs/ProjectsManager/data/project.json
[
{ "address" : [ " 3156 Dusty Highway",
" Teaneck New Jersey 07009-6370 US"
],
"id" : "a-gran-goodn",
"name" : "Grania Goodner",
"phone" : " (862) 531-9163"
},
{ "address" : [ " 62 Red Fawn Moor",
" Rodney Village West Virginia 25911-8091 US"
],
"id" : "b-aime-defranc",
"name" : "Aimery Defranco",
"phone" : " (681) 324-9946"
}
]
app.js
var projectsApp = angular.module('projects', ['ngResource']);
projectsApp.config(function($routeProvider) {
$routeProvider
.when('/', {
controller: 'ProjectListCtrl',
templateUrl: 'partials/projectlist.html'})
.when('project/:id', {
controller: 'ProjectDetailCtrl',
templateUrl: 'partials/projectdetail.html'
})
.otherwise('/');
});
projectsApp.factory('Project', function($resource) {
return $resource('data/project.json/:id', {id: '#id'});
});
projectsApp.controller('ProjectListCtrl', function(Project, $scope) {
$scope.projects = Project.query();
console.log($scope.projects);
});
projectsApp.controller('ProjectDetailCtrl', function(Project, $routeParams, $scope) {
$scope.project = $routeParams.id
? Project.get({id: $routeParams.id})
: new Project();
});
partials/projectlist.html
Add new item
<ul class="unstyled">
<li ng-repeat="project in projects">
<div class="well">
<h2><small>{{project.id}}</small> {{project.name}}</h2>
View Info for {{project.name}}
</div>
</li>
</ul>
partials/projectdetails.html
<h3>Information</h3>
<p>Name: {{project.name}}</p>
<p>Phone Number: {{project.phone}}</p>
<p ng-repeat="line in project.address">{{line}}</p>
index.php
<?php
header('Access-Control-Allow-Origin: *');
?>
<!doctype html>
<html ng-app="projects">
<head>
<meta charset="utf-8">
<title ng-bind="title" ng-cloak>Restaurant —</title>
<link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
</head>
<body ng-controller="ProjectListCtrl">
<a class="brand" href="#">Projects Manager</a>
<div id="app-container" class="container-fluid">
<div class="row-fluid">
<div class="span12" id="main" ng-view>
</div><!--/.span12-->
</div><!--/.row-fluid-->
<footer>Copyright Projects © 2013</footer>
</div><!--/.container-->
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<!-- Don't forget to load angularjs AND angular-resource.js -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.js></script>
<!--Controllers-->
<script src="app.js"></script>
</body>
</html>
Since you can't query against a raw JSON file like you can with RESTful-style URLs (which is what $resource is built to do), you can instead get a copy of the JSON and then build your own query, get, etc. that looks at the data and returns the right thing. It's a bit tricky because you also want to support new Project, which doesn't really make sense when using a file-backed store, but this example supports it:
projectsApp.factory('Project', function($http) {
// Create an internal promise that resolves to the data inside project.json;
// we'll use this promise in our own API to get the data we need.
var json = $http.get('project.json').then(function(response) {
return response.data;
});
// A basic JavaScript constructor to create new projects;
// passed in data gets copied directly to the object.
// (This is not the best design, but works for this demo.)
var Project = function(data) {
if (data) angular.copy(data, this);
};
// The query function returns an promise that resolves to
// an array of Projects, one for each in the JSON.
Project.query = function() {
return json.then(function(data) {
return data.map(function(project) {
return new Project(project);
});
})
};
// The get function returns a promise that resolves to a
// specific project, found by ID. We find it by looping
// over all of them and checking to see if the IDs match.
Project.get = function(id) {
return json.then(function(data) {
var result = null;
angular.forEach(data, function(project) {
if (project.id == id) result = new Project(project);
});
return result;
})
};
// Finally, the factory itself returns the entire
// Project constructor (which has `query` and `get` attached).
return Project;
});
You can use the results of query and get like any other promise:
projectsApp.controller('ProjectListCtrl', function(Project, $scope) {
$scope.projects = Project.query();
});
projectsApp.controller('ProjectDetailCtrl', function(Project, $routeParams, $scope) {
$scope.project = $routeParams.id
? Project.get($routeParams.id)
: new Project();
});
Note the change to Project.get($routeParams.id); also, the updated Plunker also fixes a problem in your $routeProvider configuration.
This is all demonstrated here: http://plnkr.co/edit/mzQhGg?p=preview
i will paste here a generic code i use to fetch json from your local or a remoteserver maybe it will help you:
it uses a factory that you can call when you need it.
app.factory('jsonFactory', function($http) {
var jsonFactory= {
fromServer: function() {
var url = 'http://example.com/json.json';
var promise = $http.jsonp(url).then(function (response) {
return response.data;
});
return promise;
},
hospitals: function() {
var url = 'jsons/hospitals.js';
var promise = $http.get(url).then(function (response) {
return response.data;
});
return promise;
}
};
return jsonFactory;
});
Then when you need to call it:
function cardinalCtrl(jsonFactory, $scope, $filter, $routeParams) {
jsonFactory.hospitals().then(function(d){
$scope.hospitals=d.hospitals;
});
jsonFactory.fromServer().then(function(d){
$scope.fromServer=d.hospitals;
});
}

Categories