Angular webapp using ngRoute executing controller constructor twice - html

I am using ngRoute.
index.html :
<body ng-app="mainApp" ng-controller="AboutController">
<div id="header">
<h1>Employee Gallery</h1>
</div>
<div id="leftpanel">
<!--a href="#/displayEmp" id="load">Display</a><br-->
Display<br>
Insert<br>
Delete
</div>
<!-- Angular dynamic content-->
<div ng-view id="section">
</div>
main.js :
var app = angular.module('mainApp',['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/display', {
templateUrl: 'pages/display.html',
controller: 'DisplayController'
})
.when('/', {
templateUrl: 'pages/about.html',
controller: 'AboutController'
});
/*.otherwise({
redirectTo: '/'
});*/
});
app.controller('AboutController', function($scope) {
console.log("In AboutController");
$scope.msg = "This app performs CRUD operations";
});
If ng-app="mainApp" is specified in the body tag of index.html as shown, 'In AboutController' is printed twice.
If ng-app="mainApp" is moved to
then it is printed once, as should be.
In both cases, output is the same.
1) But need to understand why the controller is executed twice in one case and once in the other.
2) In display.html, console.log is not getting printed within tag.

You're running the ctrl code twice. Once in the <body> tag and another in the route definition. You don't need to define ng-controller="AboutController" in <body>, the router is going to do that for you in your template html, which is templateUrl: 'pages/about.html' for that route.
When using the router you don't need to explicitly set an ng-controller directive on an html element like you have above, the router declares it for you.
<body ng-app="mainApp">
<div id="header">
<h1>Employee Gallery</h1>
</div>
<div id="leftpanel">
<!--a href="#/displayEmp" id="load">Display</a><br-->
Display<br>
Insert<br>
Delete
</div>
<!-- Angular dynamic content-->
<div ng-view id="section"></div>
</body>
Further reading: Combating AngularJS executing controller twice

Related

How to pass parameters to reactjs app hooked in as webpart of a site?

I built a reactjs app which i want to use as simple webpart of my site.
Means my site can have multiple webparts (also of the same reactjs app) like a weather app.
By default i defined the element id where the reactjs app should hook in.
But now i want to pass this id as parameter to the reactjs app so that i can have the app multiple times on my site.
Here is an example of what my code should lokks like:
<html>
<head>
</head>
<body>
<div class="some-div">
<div id="FirstWebPart">
<!-- Place where the app should hook in -->
<script src="path/to/reactjs/app.js"></script>
</div>
</div>
<div class="some-other-div">
<div id="SecondWebPart">
<!-- Place where the app should hook in again -->
<script src="path/to/reactjs/app.js"></script>
</div>
</div>
</body>
</html>
Important is that both apps are referencing the same source .js file but have different configs.
E.G.
The first webpart should show the weather of country 1, the second webpart should show the weather of country 2.
The logic is the same but the parameter changes the content.
Any ideas how i could do this?
I'd do it via a function that would then instantiate the React part and connect it with the provided root element. The same function could also receive the country code.
<html>
<head>
</head>
<body>
<div class="some-div">
<div id="FirstWebPart">
</div>
</div>
<div class="some-other-div">
<div id="SecondWebPart">
</div>
</div>
<script src="path/to/reactjs/app.js"></script>
<script>
myapp(document.getElementById("FirstWebPart"), "ZA");
myapp(document.getElementById("SecondWebPart"), "BR");
</script>
</body>
</html>
Not sure if "webpart" refers to ASP.NET, but I didn't see any "ASP.NET" tag on your question.
if I understand the question correctly, you need to create 2 react components: root, child. pass the country and weather from root to child
root:
import React from 'react';
import Child from './Child';
const root = () => {
const arr = [{country: 'US', weather: 'sunny'}, {country: 'UK', weather: 'rainy'}];
return <body>
{arr.map( a=><Child country={a.country} weather={a.weather} /> )}
</body>
}
export default root;
Child:
import React from 'react';
const Child = (props)=> {
return <div class="some-div">
<div id={props.country}>
<span>{props.weather}</span>
</div>
</div>
}
export default Child;

AngularJS - How to get the name of the current HTML template file for the ui-view?

My app shows an HTML page with multiple views, and each view can be conditionally built from multiple HTML templates.
I want to edit each HTML file and add a few lines at the top, something like
<div ng-if="showFileNames”>
<hr>
<p>Start of file {{how do I get the file name}}</p>
<hr>
</div>
And maybe the same at the footer.
Thus, by setting $scope. showFileNames to true, I could switch the display of file names on/off and see how my page is actually composed (is this clear, or should I add some ascii art?).
I could just hard code {{how do I get the file name}} in each file, but doing it dynamically means that I can more easily add the code to each file, plus it guards against files being renamed.
Can it be done? If so, how?
[Update] I just realized that the question didn't explain well enough. Sorry.
I need to stress that part where I said
each view can be conditionally built from multiple HTML templates
While the view is state based, its contents are built from different <ng-include> files, based on data.
So, state A might include A.html, but, based on the data, that view might <ng-include> B.html, C.html and E.html, or it might <ng-include> F.html, G.html and H.htFl - and I want to show the file name of each at the head & foot of the part of the view shown by each file
Update: You may have templates loaded with ui-view and ng-include. The example given bottom of this answer has a nice generic directive to return the corresponding template name even though if you nested ui-view and ng-include together. Click through "Home", "About" and "Named View" link inside "About".
Few theory goes below,
If you use ui-view then you can try this with $state.current.templateUrl as below.
<div ng-if="showFileNames”>
<hr>
<p>Start of file {{$state.current.templateUrl}}</p>
<hr>
</div>
The above will work if you had defined your state as below,
.state('state1', {
url: '/state1',
templateUrl: 'partials/state1.html'
controller: 'State1Ctrl'
})
But if you had defined this as named views as below,
$stateProvider
.state('report',{
views: {
'filters': {
templateUrl: 'report-filters.html',
controller: function($scope){ ... controller stuff just for filters view ... }
}
}
}
})
Then, better you can have a method assigned with the $rootScope as below and pass the $state.current.views from the html to the function.
$rootScope.getTemplate = function(view) {
var keys = Object.keys(view);
if(keys.length === 0) return '';
return view[keys[0]].templateUrl;
};
and the html,
<div ng-if="showFileNames”>
<hr>
<p>Start of file {{getTemplate($state.current.views)}}</p>
<hr>
</div>
But you can have a look at the below generic directive which covers ui-view, nested ui-view, named ui-view and ng-include and even a bit of complex nesting with ui-view and ng-include.
Generic directive with an example page
Click through "Home", "About" and "Named View" link inside "About"
var app = angular.module('app', ['ui.router']);
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: 'home.html',
controller: 'TestController'
})
.state('about', {
url: '/about',
templateUrl: 'about.html',
controller: 'TestController'
})
.state('about.named', {
url: '/named',
views: {
'named': {
templateUrl: 'named.html',
controller: 'TestController'
}
}
});
}
]);
app.controller('TestController', function($scope) {
});
app.directive('templateName', ['$state', function($state) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var templateName = $state.current.templateUrl;
var includesParent = $(element).closest('[ng-include]');
if(includesParent && includesParent.length > 0) {
if(includesParent.find('[ui-view]').length === 0) {
templateName = scope.$eval(includesParent.attr('ng-include'));
}
}
if(!templateName && $state.current.views) {
var uiViewParent = $(element).closest('[ui-view]');
var viewName = $state.current.views[uiViewParent.attr('ui-view')];
if(viewName) {
templateName = viewName.templateUrl;
}
}
element.html(templateName);
}
};
}]);
angular.bootstrap(document, ['app']);
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<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.2.23/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router.min.js"></script>
<div>
<!-- NAVIGATION -->
<nav class="navbar navbar-inverse" role="navigation" ng-include="'nav.html'">
</nav>
<!-- MAIN CONTENT -->
<div class="container">
<!-- THIS IS WHERE WE WILL INJECT OUR CONTENT ============================== -->
<div ui-view></div>
</div>
<script type="text/ng-template" id="home.html">
<h3>Home Page</h3>
<p>Start of file: <span template-name></span></p>
</script>
<script type="text/ng-template" id="about.html">
<h3>About Page<h3>
<p>Start of file: <span template-name></span></p>
<div ng-include="'aboutUs.html'"></div>
</script>
<script type="text/ng-template" id="aboutUs.html">
<h3>About us<h3>
<p>Start of file: <span template-name></span></p>
<a ui-sref="about.named">Named View</a>
<div ui-view="named"></div>
</script>
<script type="text/ng-template" id="named.html">
<h3>Named View<h3>
<p>Start of file: <span template-name></span></p>
</script>
<script type="text/ng-template" id="nav.html">
<div class="navbar-header">
<a class="navbar-brand" ui-sref="#">Start of file: <span template-name></span></a>
</div>
<ul class="nav navbar-nav">
<li><a ui-sref="home">Home</a></li>
<li><a ui-sref="about">About</a></li>
</ul>
</script>
</div>

Unable to fetch data from controller in multiple html page in angularjs

I am trying to make a webpage.In that when a user login , a new html page("dashboard.html")opens in the view. This "dashboard.html" contains some links(links to other html pages).When user clicks on them a new html(say "page_3.html") page should open with data that is present in controller.this "page_3.html" is not fetching data from controller.Below is my code.
<!-- controller.js -->
var app = angular.module('myApp',[ 'ngRoute' ]);
app.config(function($routeProvider){
$routeProvider
.when('/',{
templateUrl: 'login.html'
})
.when('/dashboard',{
templateUrl: 'dashboard.html'
})
.when('/page_3',{
templateUrl: 'page_3.html'
})
.otherwise({
redirectTo : '/'
});
});
app.controller('app', function($scope, $location){
$scope.item = "test";
$scope.submit = function(){
$location.path('/dashboard');
}
};
});
<!DOCTYPE html>
<html>
<head>
<title>Project</title>
<script src="angular.js"></script>
<script src="angular-route.js"></script>
<script src="controller.js"></script>
</head>
<body ng-app= "myApp" ng-controller="app">
<div ng-view></div>
</body>
</html>
<!-- below is login.html page -->
<div ng-controller="app">
<form action="/">
<button type="button" ng-click="submit()">Login</button>
</form>
</div>
<!-- below is my dashboard.html page -->
<div ng-controller="app">
<h1>Page_3</h1>
<div><a href='page_3.html'>page_3</a></div>
</div>
<!-- below is page_3.html page -->
<div ng-controller="app">
<p>{{item}}</p>
</div>
result : {{item}}
May I suggest avoiding the ng-controller directive and rather use the controller config object on your router?
.when('/page_3',{
templateUrl: 'page_3.html',
controller: "app"
})
There are two main problems with your code:
Enable HTML 5 mode for pushState via $locationProvider for URLs like /dashboard and /page_3
Fix the problem where route is configured for /page_3 but having a a tag pointed to /page_3.html
To get a working example:
Add a base tag
<base href="/">
Enable html5 mode via locationProvider in a config block
$locationProvider.html5Mode(true);
Fix route in dashboard.html
<!-- below is dashboard.html page -->
<div ng-controller="app">
<h1>Page_3</h1>
<div><a href='page_3.html'>page_3</a></div>
</div>
Click here for the demo / jsbin.
Other/suggestions: as Zack Briggs have suggested; using controller syntax in routes would help you come up with better code structure / design in router config, directives, or components. Also putting everything in one place is often a bad idea for a growing project.

AngularJS slider - can't get directive to work

Hi I'm trying to make a slider I got off the internet to work, but, I keep getting errors. In gallery.html, when I put the slider element at the top I get nothing, when I put it at the bottom I get errors. The error is something along the lines
Error: [$compile:tplrt] http://errors.angularjs.org/1.4.8/$compile/tplrt?p0=slider&p1=partials%2Fgallery.html
Changed my code to match the suggestions in one of the comments.
I'm no longer getting the error to do with not have just 1 root element. Now, I can't get the next and previous to work. It just redirects me to the main page.
Note:
- gallery.html and slider.html are in a folder called partials
- all the images are in a folder called img
Thanks in advance!
index.html
<!DOCTYPE html>
<html lang="en" ng-app="myapp">
<head>
<!-- ANGULAR IMPORTS -->
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/ui-router-master/release/angular-ui-router.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://code.angularjs.org/1.4.7/angular-animate.js"></script>
<!-- JS FILES -->
<script src="js/controller.js"></script>
<script src="js/directives.js"></script>
<link href='css/app.css' rel='stylesheet' type='text/css'>
</head>
<body class="container">
<div class="navbar">
<a class="brand" href="#">Quick Start</a>
<ul class="nav">
<li><a ui-sref="state1">State 1</a></li>
<li><a ui-sref="state2">State 2</a></li>
<li><a ui-sref="gallery">Gallery</a></li>
</ul>
</div>
<div class="dynamiccontent">
<div ui-view></div>
</div>
<!-- App Script -->
<script>
/** MAIN ANGULAR VAR */
var myapp = angular.module('myapp', [
/**IMPORT DEPENDENCIES*/
'ui.router',
'ngAnimate',
/**FILE DEPENDENCIES*/
'appController',
'slider.directive'
]);
/** UI-ROUTING */
myapp.config(function($stateProvider, $urlRouterProvider){
// For any unmatched url, send to /state1
$urlRouterProvider.otherwise("/state1")
$stateProvider
.state('state1', {
url: "/state1",
templateUrl: "partials/state1.html"
})
.state('state1.list', {
url: "/list",
templateUrl: "partials/state1.list.html",
controller: 'state1controller'
})
.state('gallery', {
url: "/gallery",
templateUrl: "partials/gallery.html",
controller: 'slidercontroller'
})
.state('state2', {
url: "/state2",
templateUrl: "partials/state2.html"
})
.state('state2.list', {
url: "/list",
templateUrl: "partials/state2.list.html",
controller: 'state2controller'
});
});
</script>
</body>
</html>
controller.js
var appController = angular.module('appController',[]);
appController.controller('state1controller', ['$scope', function($scope){
$scope.items = ["A", "List", "Of", "Items"];
}]);
appController.controller('state2controller', ['$scope', function($scope){
$scope.things = ["A", "List", "Of", "Items"];
}]);
appController.controller('slidercontroller', ['$scope', function($scope) {
$scope.pictures=[{src:'img1.png',title:'Pic 1'},{src:'img2.jpg',title:'Pic 2'},{src:'img3.jpg',title:'Pic 3'},{src:'img4.png',title:'Pic 4'},{src:'img5.png',title:'Pic 5'}];
}]);
directive.js
angular.module('slider.directive', [])
.directive('slider', function ($timeout) {
return {
restrict: 'AE',
replace: true,
scope:{
pictures: '='
},
link: function (scope, elem, attrs) {
scope.currentIndex=0;
scope.next=function(){
scope.currentIndex<scope.pictures.length-1?scope.currentIndex++:scope.currentIndex=0;
};
scope.prev=function(){
scope.currentIndex>0?scope.currentIndex--:scope.currentIndex=scope.pictures.length-1;
};
scope.$watch('currentIndex',function(){
scope.pictures.forEach(function(image){
image.visible=false;
});
scope.pictures[scope.currentIndex].visible=true;
});
/* Start: For Automatic slideshow*/
var timer;
var sliderFunc=function(){
timer=$timeout(function(){
scope.next();
timer=$timeout(sliderFunc,5000);
},5000);
};
sliderFunc();
scope.$on('$destroy',function(){
$timeout.cancel(timer);
});
/* End : For Automatic slideshow*/
},
templateUrl:'partials/slider.html'
}
});
gallery.html
<slider pictures="pictures"></slider>
slider.html
<div class="slider">
<div class="slide" ng-repeat="image in pictures">
<img ng-src="img/{{image.src}}"/>
</div>
<div class="arrows">
<img src="img/left-arrow.png"/>
<img src="img/right-arrow.png"/>
</div>
</div>
You are mixing several concerns here. partials/gallery.html is used as a template for your directive and your page. This leads to the directive being used itself in it's own template.
The solution is to remove your directive code from gallery.html and use it in a new file slider.html.
directive.js
.directive('slider', function ($timeout) {
return {
...
templateUrl:'partials/slider.html'
slider.html
<div class="slider">
<div class="slide" ng-repeat="image in images">
<img ng-src="img/{{image.src}}"/>
</div>
<div class="arrows">
<img src="img/left-arrow.png"/>
<img src="img/right-arrow.png"/>
</div>
</div>
gallery.html
<slider images="pictures"/>
First off - the error does have to do with the root element.
https://docs.angularjs.org/error/$compile/tplrt
Template for directive '{0}' must have exactly one root element. {1}
Now that you fixed that issue - your infinite loop is called because you're trying to populate the slider directive with gallery.html - but in that template, you're calling the slider directive. So that's an infinite loop.

angularjs directive displays json during page load

I'm building my first AngularJS dynamic form, built based on information received from a JSON file using AngularJS directive.
Everything works, my issue is that the JSON code is getting displayed while the page is loaded - once the page is loaded the JSON code disappears.
Am I doing something wrong?
Check http://plnkr.co/edit/v4jOwuF6jmZfORlNbvIB?p=preview to see the behavior, click on "Stop"/"Start" multiple times to see the behavior.
HTML code:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script data-require="angular.js#*" data-semver="1.4.0-beta.2" src="https://code.angularjs.org/1.4.0-beta.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="ViewCtrl">
<div ng-repeat="page in form.form_pages">
<div ng-repeat="field in page.page_fields" class="form-group">
<field-directive field="field" ng-form="subForm"></field-directive>
</div>
</div>
</body>
js code:
'use strict';
angular.module('myApp',[])
.controller('ViewCtrl', ['$scope', function($scope) {
var jsonStr='{"form_id":"1","form_name":"My Test Form","form_pages":{"1":{"page_id":1,"page_title":"My First Tab","page_hide":false,"page_fields":{"1":{"field_id":1,"field_title":"First Name","field_type":"textfield","field_value":"","field_required":true,"field_disabled":false},"2":{"field_id":2,"field_title":"Last Name","field_type":"textfield","field_value":"","field_required":true,"field_disabled":false},"3":{"field_id":3,"field_title":"Gender","field_type":"textfield","field_value":"0","field_required":true,"field_disabled":false},"4":{"field_id":4,"field_title":"Email Address","field_type":"textfield","field_value":"","field_required":true,"field_disabled":false},"5":{"field_id":5,"field_title":"Password","field_type":"textfield","field_value":"","field_required":true,"field_disabled":false},"6":{"field_id":6,"field_title":"Birth Date","field_type":"textfield","field_value":"1981-01-10T06:00:00.000Z","field_required":true,"field_disabled":false},"7":{"field_id":7,"field_title":"Your browser","field_type":"textfield","field_value":"2","field_required":false,"field_disabled":false},"8":{"field_id":8,"field_title":"Additional Comments","field_type":"textarea","field_value":"","field_required":true,"field_disabled":false},"9":{"field_id":9,"field_title":"I accept the terms and conditions.","field_type":"textfield","field_value":"0","field_required":true,"field_disabled":false}}}}}';
$scope.form = JSON.parse(jsonStr);
}])
.directive('fieldDirective',function($http, $compile) {
var linker = function(scope, element) {
// GET template content from path
var templateUrl = "textfield.html";
$http.get(templateUrl).success(function(data) {
element.html(data);
$compile(element.contents())(scope);
});
}
return {
template: '<div>{{field}}</div>',
restrict: 'E',
scope: {
field: '='
},
link: linker
};
})
textfield.html - the html template:
<div class="row" ng-form="subForm" ng-class="{'has-success': subForm[field.field_id].$invalid}">
<div class="col-sm-5">{{field.field_title}}:</div>
<div class="col-sm-7">
<input type="text"
placeholder="{{field.field_title}}"
ng-model="field.field_value"
value="{{field.field_value}}"
ng-required="field.field_required"
ng-disabled="field.field_disabled"
class="form-control"
id = "{{field.field_id}}"
name = "{{field.field_id}}" >
<div ng-show="subForm[field.field_id].$touched && subForm[field.field_id].$error && subForm[field.field_id].$invalid">Field '{{field.field_title}}'
<span ng-show="subForm[field.field_id].$error.required"> is required.</span>
</div>
</div>
</div>
Thank you.
http://plnkr.co/edit/YC9p0UluhHyEgAjA4D8R?p=preview
Basically instead of adding the loaded template into the element then compiling it in place I just compile the string then insert the compiled element directly
element.append($compile(data)(scope));
Seems you can still see a delay but this might be the async loading of the template causing that, would need to debug in the network panel and do some profiling or logging to see exactly what's going on.
Edit
Made a fork of the plnkr to show one with the template inlined so there's no delay fetching it with $http http://plnkr.co/edit/Tnc3VOeI8cELDJDHYPTO?p=preview instead just grabbing it synchronously from the template cache and using ng-template in a script block to have it loaded in advance.