I'm so confused about my situation. Let me briefly introduce my situation.
HTML structure. (It's just structure, full HTML is more than that but there is no any other controller except "pageController")
<body ng-app="app">
<div id="wrapper" ng-controller="pageController">
<div id="menu">
<a>.........</a>
</div>
<div id="mainView">
<ng-view></ng-view>
</div>
</div>
</body>
Of course, I properly set router.
var app = angular.module("app", ["ngRoute"])
.config(function($routeProvider){
$routeProvider
.when("/about", {
templateUrl: "about.html"
})
.when("/summary", {
templateUrl: "summary.html"
})
.when("/company", {
templateUrl: "company.html"
})
.when("/remote", {
templateUrl: "remote.html"
})
.when("/personal", {
templateUrl: "personal.html"
})
.when("/academic", {
templateUrl: "academic.html"
})
.otherwise({
redirectTo : "/about"
});
});
I didn't set any controller now. I put each controller as "pageController" before but I removed now because I think pageController will be automatically inherited as long as pageController is in parent element.
And I made custom directive.
app.directive('imageLoader', function(){
return{
restrict:'A',
link:function(scope, elem, attrs){
elem.bind('click', function(){
var fileName = "img/portfolioImages/" + attrs.ori;
scope.$parent.$parent.modalImgSrc = fileName;
scope.$parent.$parent.isModalOpen = true;
scope.$parent.$parent.$digest();
});
}
};
});
In injected html in ng-view, there is no any other controller, but it dynamically generate <img> tag like below
<div class="project-image-div">
<img image-loader ng-repeat="aImgSrc in remoteImageFiles[0]"
data-ori='{{aImgSrc}}' class='project-img-s'
ng-src='img/portfolioImages/{{aImgSrc.substring(0,aImgSrc.lastIndexOf(".")) + "_s" + aImgSrc.substr(aImgSrc.lastIndexOf("."), 4)}}'>
</div>
remoteImageFiles[0] is json object saved in pageController scope. And these dynamically added img tags are completely fine.
THE PROBLEM
As you can see in <img> tag, I am using custom directive image-loader. In directive code, I expected "scope" is same scope as pageController as long as there is no any other controller inside ng-view and I didn't give any option for scope in the custom directive.
But I printed scope object in console, I can access pageController scope object as parent of parent... WHY?????
scope.$parent.$parent.modalImgSrc = fileName;
scope.$parent.$parent.isModalOpen = true;
scope.$parent.$parent.$digest();
Thank you for reading my long post... please help... I am spending 2 days for this..
You can see full code in my personal web site. I am noob to angularJS. I am converting JQuery version of site to pure angularJS version to learn AngularJS.. It's in progress.
AngularJS version..(converting now)
http://bear-mj.com/MJKim
JQuery version..(all working)
http://bear-mj.com/MJKimJQuery/
You have 3 levels of scope created. Remember, both ng-repeat and ng-view create a new scope. In this area of your app - inside the directive:
scope is the ng-repeat scope
scope.$parent is the ng-view scope
scope.$parent.$parent is the pageController scope
Related
so I have a component with a template containing a form.
mycomponent.html:
<div>
<form name="myForm">
<!-- more html code -->
</form>
</div>
How can I access myForm inside the component controller?
Currently I'm injecting $scope to get it from that.
Or is that the only way to get the form?
Edit: Added some code to better illustrate in javascript
angular.module('example')
.component('myComponent', {
templateUrl: 'mycomponent.html',
controller: function($scope) {
$scope.myForm // This works
this.myForm // undefined, can I access it through the component scope instead of $scope somehow?
}
});
The name attribute of a form is what angular uses to decide what to bind to. So, if you're using the controllerAs syntax, you have to use that in the form name:
<body ng-controller="MainCtrl as vm">
<form name='vm.myForm'>
</form>
</body>
This will allow you to refer to it in your controller without using $scope, but only after the controller has been successfully created:
app.controller('MainCtrl', function($scope, $timeout) {
var vm = this;
console.log(vm.myForm); // undefined
$timeout(function() {
console.log(vm.myForm); // FormController object
}, 100);
});
Here is a working plunk.
Use the name syntax but also a components postLink lifecycle hook, that function is called once a template and controller have been connected see https://docs.angularjs.org/guide/component
[EDITED] My app has the following structure:
index.html
<body ng-app = "myApp" ng-controller ="mainController">
<ng-view></ng-view>
</body>
mainView.html (loaded into ng-view through routeProvider in app.js)
<div ng-include src="subview1">
<div ng-include src="subview2">
subview1 and subview2 are set within mainController (mainView's controller) as scope variables:
$scope.subview1= "templates/subview1.html";
$scope.subview2= "templates/subview2.html";
controller1 and controller2 are subview1 and subview2's controllers.
subview1.html (loaded in first div of mainView)
<div ng-controller="controller1">
<button ng-click="loadNewView()"></button>
</div>
controller1.js
.controller('controller1', function($scope){
$scope.loadNewView = function(){
$scope.$parent.subview1 = "templates/view3.html";
}
}
scope.loadNewView should load a different view (and relative controller) within the div with src="subview1" in mainView.html). Basically it's about refreshing the view itself by raplacing it with another view (and related controller).
I use $parent to update the view in subview1's parent view (i.e. mainView).
however nothing happens and if I try to use $scope.$apply() I get error (digest already in progress).
Any clue?
you can try something like this...
In your stateProvider or in your routeProvider if you using.
var mod = angular.module('example.states', ['ui.router']);
mod.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('exampleState', {
url: '/main',
templateUrl: 'mainView.html',
controller: mainController
});
}
]);
return mod;
So here you have associated your parent controller(let's say the mainController which will be the parent of all others) with its template mainView.html.
Then in your mainView.html,Load all the subview templates.
<div ng-repeat="template in templates">
<ng-include src="template.url"></ng-include>
</div>
templates is an array in your mainController which has the url or path of all your subtemplates.When you use ng-include inside the main template then all subTemplates will automaticlly become the child of the mainTemplate and its Controllers too.In a way it will inherit from the parent Controller.
So suppose if subView1.html is one of the template url you had given in ng-include.Then it will look like
<div ng-controller="subView1Controller">
//Here your code
</div>
And subview2 as
<div ng-controller="subView2Controller">
//Here your code
</div>
This way you will have multiple views on the same page with one url and different controllers with its associated templates and each will inherit from the parent controller which is mainController here.
There, might be better approach than this.
This is what i had used in my project,and its simple to keep your code simple manage.
Okay,so using routeProvider,you can use it like this
var app = angular.module("app",[]);
app.config(function($routeProvider){
$routeProvider
.when('/main',{
templateUrl:"mainView.html",
controller:mainController
})
});
app.controller("mainController",function($scope){
});
app.controller("subView1Controller",function($scope){
});
app.controller("subView1Controller",function($scope){
});
Then in your mainView.html,Load all the subview templates.
<ng-include src="yoursubtemplate1path"></ng-include>
<ng-include src="yoursubtemplate2path"></ng-include>
And then in yoursubtemplate1 use
<div ng-controller="subView1Controller">
//Here your code
</div>
Same for the other templates.
You can set the template src of the subtemplates from your mainController.
app.controller("mainController",function($scope){
$scope.templatesrc="/app/template1.html";
});
And then use it in your template,where you are using ng-include directive.
<ng-include src="templatesrc"></ng-include>
Its better to store template url's in an array and use ng-repeat directive like i had stated before,if you are loading more templates.
And if you want to show the div on some button click lets say in parent controller then use ng-if in the sub-view main and make it true on button click.
This answer is regarding your updated question.
The solution which you had used before,will load all temlplate and once in ng-include and its associated controller making the mainController as parent.
But if you want to load a different view with its newController then you can try something like this.
Just add one more route and call on your event click,but remember this newView's Controller will have no parent-child relation with the mainView's controller.
var app = angular.module("app",[]);
app.config(function($routeProvider){
$routeProvider
.when('/main',{
templateUrl:"mainView.html",
controller:mainController
})
.when('/anyName',{
templateUrl:"templates/view3.html",
controller:temp3Controller
})
});
And in your controller1.js
.controller('controller1', function($scope){
$scope.loadNewView = function(){
$location.path('/anyName');
}
}
Inject location service in controller1.
I finally found the solution.
The tricks is using
$scope.$parent.$parent.subview1 = "templates/view3.html";
instead of
$scope.$parent.subview1 = "templates/view3.html";
since, basically:
ng-include is the child of mainView
subview1 is the child of ng-include
I need that code will be called in controller ChatController, not in global application:
.config(function($routeProvider){
$routeProvider.when("/chat/dialog/:id",
{
templateUrl: "/template/chat/active_dialog.html",
controller: "ChatController"
}
);
})
How I can do it?
I tried (template is not loaded in div):
Angular JS:
$scope.selectDialog = function (id, event){
$scope.template = '/template/chat/active_dialog.html';
});
HTML:
<div ng-include src="{{template}}"></div>
I agree with #Nano,all the providers that are used angular are injected and used in
.config,you directly cannot use it in your controller.
I'm using AngularUI Router with Bootstrap. I have two views within the same state, and want to scroll to the second view when a button is clicked. I don't need to do any scrolling when the initial state and views load. I want the page to scroll to #start when the "Add More" button is clicked. I've attempted to use $anchorScroll to accomplish this, but am not having any luck.
Any suggestions on a good way to accomplish this?
HTML:
<!-- index.html -->
<div ui-view="list"></div>
<div ui-view="selectionlist"></div>
<!-- list.html -->
<div id="scrollArea"><a ng-click="gotoBottom()" class="btn btn-danger btn-lg" role="button">Add More</a>
<!-- selectionlist.html -->
<div class="row" id="start"></div>
Javascript for Controller:
myApp.controller('SelectionListCtrl', function (Selections, $location, $anchorScroll) {
var selectionList = this;
selectionList.selections = Selections;
this.selectedServices = Selections;
selectionList.gotoBottom = function() {
$location.hash('start');
$anchorScroll();
};
});
Javascript for Routes:
myApp.config(function ($stateProvider, $urlRouterProvider, $uiViewScrollProvider) {
$uiViewScrollProvider.useAnchorScroll();
$urlRouterProvider.otherwise('/');
$stateProvider
.state('selection', {
url: '/selection',
views: {
'list': {
templateUrl: 'views/list.html',
controller: 'ProjectListCtrl as projectList'
},
'selectionlist': {
templateUrl: 'views/selectionlist.html',
controller: 'SelectionListCtrl as selectionList'
}
}
})
Yes, it is possible to autoscroll in AngularUI Router without changing states.
As mentionned previously in my comment, you need to call the scrolling function with an ng-click="gotoBottom()" instead of an ng-click="gotoSection()"
Also, the function definition gotoBottom() must be in the ProjectListCtrl, not in the SelectionListCtrl. This is because the call gotoBottom() happens in the list view:
'list': {
templateUrl: 'list.html',
controller: 'ProjectListCtrl as projectList'
}
As gotoBottom() is called from the list.html view, the corresponding controller in $stateProvider must be the one where you define gotoBottom().
Here are two working ways of accomplishing your goal:
1. You inject $scope inside the controller ProjectListCtrl. You then define the $scope.gotoBottom function in the same controller.
The scope is the glue between the controller and the view. If you want to call a controller function from your view, you need to define the controller function with $scope
app.controller('ProjectListCtrl', function ($location, $anchorScroll,$scope) {
var selectionList = this;
//selectionList.selections = Selections;
//this.selectedServices = Selections;
$scope.gotoBottom = function() {
console.log("go to bottom");
$location.hash('start');
$anchorScroll();
};
});
In the list.html view, you can then call the $scope.gotoBottom function just with gotoBottom(): ng-click="gotoBottom()"
2. Or you use the controller as notation, as when you wrote ProjectListCtrl as projectList.
this.gotoBottomWithoutScope = function(){
$location.hash('start');
$anchorScroll();
};
With this notation, you write this.gotoBottomWithoutScope in the ProjectListCtrl. But in the view, you must refer to it as projectList.gotoBottomWithoutScope().
Please find a working plunker
To learn more about the this and $scope notations, please read this:
Angular: Should I use this or $scope
this: AngularJS: "Controller as" or "$scope"?
and this: Digging into Angular’s “Controller as” syntax
I've this routes.
// index.html
<div ng-controller="mainCtrl">
<a href='#/one'>One</a>
<a href='#/two'>Two</a>
</div>
<div ng-view></div>
And this is how I'm loading the partials into my ng-view.
// app.js
var App = angular.module('app', []);
App.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/one', {template: 'partials/one.html', controller: App.oneCtrl});
$routeProvider.when('/two', {template: 'partials/two.html', controller: App.twoCtrl});
}]);
When I click the links, it shows me the appropriate markup inside the ng-view. But when I try to include partials/two.html inside partials/one.html using ng-include, it shows it properly but creates a different scope so I'm not able to interact with it.
// partials/two.html - markup
<div ng-controller="twoCtrl">I'm a heading of Two</div>
// partials/one.html - markup
<div ng-controller="oneCtrl">I'm a heading of One</div>
<div ng-include src="'partials/two.html'"></div>
How do I resolve this problem? Or Is there any other way to achieve the same result?
You can write your own include directive that does not create a new scope. For example:
MyDirectives.directive('staticInclude', function($http, $templateCache, $compile) {
return function(scope, element, attrs) {
var templatePath = attrs.staticInclude;
$http.get(templatePath, { cache: $templateCache }).success(function(response) {
var contents = element.html(response).contents();
$compile(contents)(scope);
});
};
});
You can use this like:
<div static-include="my/file.html"></div>
The documentation for ngInclude states "This directive creates new scope." so this is by design.
Depending on the type of interaction you are looking for you may want to take a look at this post for one way to share data/functionality between the two controllers via a custom service.
So this isn't an answer to this question but i made it here looking for something similar and hopefully this will help others.
This directive will include a partial without creating a new scope. For an example you can create a form in the partial and control that form from the parent controller.
Here is a link to the Repo that i created for it.
good luck :-)
-James Harrington
You can actually do this without using a shared service. $scope.$emit(...) can dispatch events to the $rootScope, which can listen for them and rebroadcast to the child scopes.
Demo: http://jsfiddle.net/VxafF/
Reference:
http://www.youtube.com/watch?v=1OALSkJGsRw (see the first comment)