test.html (my template):
<span data-ng-bind="emailSent.info"></span>
js:
$scope.resetPasswordEmail = "my#email.here";
// info is taken from a database with different languages
$scope.emailSent = {
info: getInfoFromDatabase() // returns: 'Confirmation email sent to {{resetPasswordEmail}}, check your email.'
};
angular.element(document.body).append($compile($templateCache.get('test.html'))($scope));
However this results in the following in the span on the page:
Confirmation email sent to {{resetPasswordEmail}}, check your email.
I'm attempting to do "nested" scope variables. Do I have to re-compile the compiled template again. Or is there a proper angularjs way of achieving this.
Based on your updated question, I see why you're doing this.
The key to do it is to use $interpolate:
angular.module('app', []).
controller('Ctrl', function($scope, $interpolate, $compile){
$scope.resetPasswordEmail = "my#email.here";
$scope.emailSent = {
info: $interpolate(getInfoFromDatabase())($scope)
};
function getInfoFromDatabase(){
return 'Confirmation email sent to {{resetPasswordEmail}}, check your email.'
}
angular.element(document.body).append($compile('<span data-ng-bind="emailSent.info"></span>')($scope));
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="Ctrl"></div>
Related
I have a html page with a link as follows:
<div ng-if="!adminCtrl.valid">
<div><a target="_blank" ng-href="https://api.instagram.com/oauth/authorize/?client_id=xxx&redirect_uri=http://localhost:8888/igSuccess.html&response_type=token">Authorize to Instagram</a><br/></div>
</div>
This goes to redirect page on success where the code is
<div ng-controller="AdminController">
<h2>You can close this tab/window</h2>
</div>
The control is same for both pages as follows:
app.controller('AdminController', ['$scope','$routeParams','$location', function($scope,$routeParams,$location){
var actrl = this;
actrl.valid = false;
var token = $location.absUrl();
if(token.indexOf('access_token') > -1){
console.log('found token so will do special');
actrl.valid = true;
$scope.$apply();
}
}}
I am expecting the link to disappear once the new page opens as i am updating the valid variable value.
i know the flaw seems to be the cross page communication. so how to deal with it?
Controllers are 'flushed' when you change views. To keep data from a view/controller to another, store your data within a Service.
UPDATE
controller:
app.controller('AdminController', [
'$scope', '$routeParams', '$location', 'ExampleService', function ($scope, $routeParams, $location, ExampleService) {
var actrl = this;
// Watches the service's value for changes and applies it to the controller
$scope.$watch(function(){return ExampleService.valid}, function(newValidValue){
actrl.valid = ExampleService.valid;
});
var token = $location.absUrl();
if (token.indexOf('access_token') > -1) {
console.log('found token so will do special');
ExampleService.valid = true;
// No need for this
// $scope.$apply();
}
}
}
Service:
app.service('ExampleService', [
function () {
//All properties here are kept through-out your app's life time
this.valid = false; // Init to false
}
}
To share data between Controllers in Angular JS, use a named Service to encapsulate the data. In your case, I would typically define an Auth service that provides a few methods for getting and setting the access_token for a user:
module.factory('Auth', function(){
return {
isValid: function(){ /* Check that a User is authenticated... */ },
setToken: function(token){ /* Store the token somewhere... */ },
getToken: function(){ /* Fetch the token from somewhere... */ }
};
});
To share data across "pages" -- tabs or windows in your browser -- even in a Single Page Application (SPA) like this, store the data in cookies or localStorage. You can use angular-local-storage by grevory (GitHub) to abstract the details of using localStorage with a cookie fall-back in non-compatible browsers.
The reason that one page cannot see the valid value defined in the other is because each page gets a separate instance of AdminController, each of which get their own separate instance of $scope tied to their respective DOM elements. Setting valid on the $scope of the redirect landing page has not effect on the completely detached $scope instance in the originating page.
You'd encounter similar difficulties with a trivial same-page example (CodePen):
angular.module('scope-example', [])
.controller('ExampleCtrl', function($scope) {
$scope.value = 'Initial Value';
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form class="pure-form" ng-app="scope-example">
<fieldset ng-controller="ExampleCtrl">
First instance of <code>ExampleCtrl</code>:
<br>
<input ng-model="value">
<label>{{value}}</label>
</fieldset>
<fieldset ng-controller="ExampleCtrl">
Second instance of <code>ExampleCtrl</code>:
<br>
<input ng-model="value">
<label>{{value}}</label>
</fieldset>
<fieldset ng-controller="ExampleCtrl">
Third instance of <code>ExampleCtrl</code>:
<br>
<input ng-model="value">
<label>{{value}}</label>
</fieldset>
</form>
Even though each of the <fieldset> elements have identical ng-controller directives associated, each gets its own instance of ExampleCtrl and $scope, so the value property isn't shared between them. This holds true for any directive.
I have a json file of objects that store the properties to be used in a directive.
I want to use the json obj model value in the directive, but nothing I am trying is working.
Anyone have any ideas what I am doing wrong / missing? I find this very confusing.
Hope someone can help been trying this for days now!
Edit::
I have a $http service that gets and returns the Json object and I can access the properties fine.
I am specifically trying to use the value of the json obj model property -- "model" : "ticketData.contactname" as the dynamic value of the ng-model.
If I just use the ticketData.contactname obj then it works fine and I can edit the model value, but if I try and use the string from the Json obj then it just prints the string into the input box.
I do not know what to do. I am sure it is something basic I am missing.
Thanks in advance
Json sample:
[
{
"inputsContact" : [
{
"labelName" : "Contact Name",
"placeholder" : "Enter your name",
"model" : "ticketData.contactname",
"type" : "text"
}
}
]
Html sample:
<text-input-comp inputdata="contactName" ng-model="contactModel"> </text-input-comp>
Directive text-input-comp:
.directive('textInputComp', [ '$compile', function($compile){
return {
restrict: 'E',
scope: {
inputData: '=inputdata',
modelData: '=ngModel'
},
templateUrl: '/app/views/partials/components/textInputComp.html'
}
}]);
Directive template sample:
<label> {{ inputData.labelName }} </label>
<input type="text" ng-model="modelData" ng-model-options="{ getterSetter: true }" placeholder="{{ inputData.placeholder }}" />
<div ></div>
Controller sample:
$scope.contactName = $scope.inputData[0].inputsContact[0];
$scope.contactModel = $scope.inputData[0].inputsContact[0].model;
i think u need to get the json file first then do all the manupilation
have a look at this code
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="customersCtrl">
<ul>
<li ng-repeat="x in myData">
{{ x.Name + ', ' + x.Country }}
</li>
</ul>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('customersCtrl', function($scope, $http) {
$http.get("customers.json").then(function (response) {
$scope.myData = response.data.records;
});
});
</script>
</body>
</html>
What u were missing is this
Just replace the customer.json file with your json and u are good to go
and
1.$http is service responsible for communicating with other file. $http says get file from ‘js/data.json’ and if the operation is a ‘success’ then execute a function holding the ‘data’ which it got automatically from data.json file
to make u understand.
2.A one more line above: [‘$scope’, ‘$http’, function($scope, $http){ … }] is little bit tricky: it takes an array holding two objects one is $scope and other is a service i.e $http. The reason we have this in array is angularJS automatically minifies code which means it removes extra spaces and shorten variable names for faster performance but sometimes this minification causes trouble so we just told controller NOT to minify $scope, $http services and function inside array.
This plnkr : https://plnkr.co/edit/BjETLN7rvQ1hNRIm51zG?p=preview binds content to three divs within loop : <div ng-repeat="id in ids">
src :
{ "content" : "divContent" , "id" : "r1" }
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="FetchCtrl">
<div ng-repeat="id in ids">
<div ng-bind="content" ></div>
</div>
</div>
</body>
</html>
// Example of how to call AngularJS $http service and
// process the returned promise
function FetchCtrl($scope, $http, $q) {
$scope.ids = ["r1", "r2", "r3"];
$scope.ids = $scope.ids.map(function(id){
var getString = 'http-hello1.html?id='+id
return $http.get(getString);
});
$scope.responses = [];
$q.all($scope.ids).then(function (values) {
var counter = 0;
values.map(function(m){
$scope.content = m.data.content;
})
})
}
But how bind the result of each get request to the specific div ?
Could add id : <div id="{{id}}" ng-bind="content" ></div> but this means I need to maintain a map of id,value entries ? Is there an idiomatic angularjs way to achieve this ?
I think a directive which dynamically fetches your content might be the answer for you.
angular.module('whateverYourModuleNameIs')
.directive('dynamicRow', ['$http', '$interval', dynamicRowDirectiveFn]);
function dynamicRowDirectiveFn($http, $interval) {
return {
restrict: "EA", // I guess this is your choice how it renders
scope: {
id: '=' // you could grab the id and use it in your request
},
link: function linkFn(scope, element, attrs) {
// Use $interval to repeatedly fetch content
var repeatFetchWhichReturnsAPromise = $interval(fetchNewContent, 60000 * 15) //Executes function every x milliseconds.
// Function that executes every time your interval occurs
function fetchNewContent() {
$http.get('urlYouNeedToFetch'+id).then(
fetchNewContentSucess, fetchNewContentError
);
}
function fetchNewContentSuccess(responseObject){
//This sets your new HTML based on promise success
element = responseObject.data;
}
function fetchNewContentError(responseObject){
//If its a bad request we probably either want to stop repeating
// You can choose to do something else
$interval.cancel(repeatFetchWhichReturnsAPromise);
}
}
}
}
So Instead of using $q.all(), Id recommend individually fetching the content based on a timer or specific trigger. The downside with $q.all() is that if one of the promises fail, they all fail.
In terms of knowing what specific URL the directive needs to fetch, you'll have to provide that information to the directive to be used.
This is a very rough example of a directive that you could write. The upside is that you don't have to worry about bind-unsafe-html or include ngSanitize, you are instead just resetting the value of element inside your link function.
As I don't have a better picture of what you are trying to accomplish from a feature/product standpoint I can only suggest this based on the info provided.
I bind data of two different scopes to the same common service data. When I update the data through controller 1, the data in controller 2 is not refreshed in template.
Example showing the issue :
<body ng-app="demoShareBindApp">
<div ng-controller="FirstCtrl as first">
Set share data to : <a href ng-click="setShareDataTo('Me')">"Me"</a>
- <a href ng-click="setShareDataTo('Myself')">"Myself"</a>
- <a href ng-click="setShareDataTo('I')">"and I"</a>
</div>
<div ng-controller="SecondCtrl as second">
Text entered : {{sShareData}}
<br>
<br><a href ng-click="revealShareData()">Reveal data</a>
</div>
<script src="bower_components/angular/angular.js"></script>
<script>
angular
.module('demoShareBindApp', [])
.service('myService', function () {
return {
shareData: null
}
})
.controller('FirstCtrl', ['$scope', 'myService', function ($scope, myService) {
$scope.setShareDataTo = function(content) {
myService.shareData = content;
};
}])
.controller('SecondCtrl', ['$scope', 'myService', function ($scope, myService) {
$scope.sShareData = myService.shareData;
$scope.revealShareData = function() {
console.log(myService.shareData);
}
}]);
</script>
</body>
The Text entered : {{sShareData}} is never updated whereas clicking "Reveal data" shows the right share data in console.
I can't find any clue in other SO post on this particular subject. I guess it could be a matter of "$watch/$digest" but I can't figure out what is really going on here.
Any detailed explanation welcome !
You are sharing data through service. When data in changed under first controller, you set this updated data to service but second controller does not know that shared data referenced by service has changed, so we need to notify the second controller that data has changed or we can shared data through events
I created a fiddle, check it
https://jsbin.com/midebu/edit?html,js,output
First approach. Using service
$scope.setShareDataTo = function(content) {
myService.shareData = content;
$rootScope.$broadcast('datacChanged');
};
In second cntroller
$rootScope.$on('dataChanged', function(){
// get updated data
$scope.sShareData = myService.shareData;
})
Other way is that, we do not need to use service , we can simply pass that shared data using events
$scope.setShareDataTo = function(content) {
$rootScope.$broadcast('datacChanged', content);
};
And in Second controller
$rootScope.$on('dataChanged', function(event, data){
// get updated data
$scope.shareData = data;
})
I have an Angular template that pulls in two HTML pages ...
sidebar.html
content.html
In content.html I use a controller to make an API call that gives a true/false response and based on that response I render different text. Is there a way that I can gain access to the same true/false response from within sidebar.html? I would rather not duplicate the API call done in the controller for content.html.
content.html
<div data-ng-controller="AuthorizeController">
<div ng-if="isUserAuthorized">
text if User is authorized ...
</div>
<div ng-if="!isUserAuthorized">
text if User is NOT authorized ...
</div>
</div>
AuthorizeController.js
(function () {
var dependencies = [
'$scope',
AuthorizeController
];
module.controller('AuthorizeController', dependencies);
function AuthorizeController($scope) {
// Code to determine if authorized omitted for brevity
if (true) {
$scope.isUserAuthorized = true;
} else {
$scope.isUserAuthorized = false;
}
}
})();
I should add that the portion of the controller that is determining if the user is authenticated does so via an API call. I am hoping to make the API call only once.
Use an angular service (?) to share the authentication logic between your controllers.
Demo: http://plnkr.co/edit/n3iOSkAefxy8xRW7vzFG?p=preview
javascript
var app = angular.module('app', []);
app.controller('mainCtrl',function($scope, authenticationService) {
$scope.isAuthenticated = authenticationService.isAuthenticated();
}
);
app.controller('sidebarCtrl',function($scope, authenticationService) {
$scope.isAuthenticated = authenticationService.isAuthenticated();
}
);
app.factory('authenticationService',function() {
return {
isAuthenticated: isAuthenticated
}
function isAuthenticated(){
// Place your logic here
return true;
}
}
);
html
<body ng-app="app">
<div>
<h1>Obtaining scope variable from separate HTML file</h1>
</div>
<div ng-controller="mainCtrl">
{{ isAuthenticated }}
</div>
<div ng-controller="sidebarCtrl">
{{ isAuthenticated }}
</div>
</body>
There are three options:
1st solution:
Move the logic of Auth to a service, and use dependency injection to inject the service into AuthorizeController and SidebarController.
2nd solution:
Use $broadcast/$emit from AuthorizeController and $on on the SidebarController. Although this is not a good solution because then you will have two controllers tightly coupled.
3rd solution: (Even better than the 1st)
Write the sidebar as a directive and inject the Auth Service to that.