Consider the following:
<script type="text/ng-template" id="myTemplateName">
{{item.SomeProperty}}
<script>
<div ng-repeat="container in List">
<div ng-repeat="item in container.Items">
<!-- CASE 1 -->
<div ng-include="'myTemplateName'"></div>
</div>
<!-- CASE 2 -->
<div ng-include="'myTemplateName'" />
</div>
The code above works in case1, but not in case2: case 1 will work because the template uses item, which is made available by the ng-repeat statement outside of the template, case 2 doesn't work because there is no item, instead i want it to use container.SomeProperty.
Maybe i am misusing angular includes, but i wanted to use them like partials in ASP.Net MVC. There, you can define a partial and you are able to pass in a model.
Is there any way in angular that allows me to set what item means inside the template?
Problem solved using Matt's answer:
module.controller("ItemController", ['$scope', function ($scope) {
$scope.templateitem = ($scope.$parent.item) ? $scope.$parent.item : $scope.$parent.$parent.container.Item;
}]);
Still, it feels kind of dirty: the controller needs to know how it can be used. It would be better if i could pass this to the controller from the outside.
A better approach:
I didnt really like the solution above, because the controller needs to know how it will be used, so i used a directive:
app.directive("opportunity", function () {
return {
restrict: "E",
templateUrl: "opportunityTemplate",
scope: { templateitem: "=model" }
};
});
In view:
<script type="text/ng-template" id="opportunityTemplate">
{{templateitem.SomeProperty}}
</script>
<opportunity model="container.Item"></opportunity>
<opportunity model="somethingElse.Item"></opportunity>
Now all i need to find out is how i can pass the templatename into the directive, and i can make a re-usable "partial" directive (please tell me if i am reinventing the wheel here?)
What you need to use is ng-controller, or some routing system like ui-router that connects partials to controllers through route definitions.
For the first example, when you add your partial, also specify a controller which takes care of dealing with the model:
HTML:
<div ng-controller="MyCtrl" ng-include="'myTemplateName'" />
<!-- inside the template -->
<div>{{item.somekey}}</div>
Controller:
angular.module('myapp.ctrl', [])
.controller('MyCtrl', ['$scope', function($scope){
$scope.item = { somekey: 'somevalue' };
}]);
In the second example, use a routing system like ui-router. An example of that can be seen here, where certain partials are attached to controllers and specific urls:
https://github.com/angular-ui/ui-router/tree/gh-pages/sample
Related
I am using AngularJS 1.6.9 with AngularJS Material. What I want to do is to add a menu item basic usage. The problem is that it didn't work because my structure is different.
Documentation of material says (I never seen it before):
angular
.module('menuDemoBasic', ['ngMaterial'])
.config(function($mdIconProvider) {
$mdIconProvider
.iconSet("call", 'img/icons/sets/communication-icons.svg', 24)
.iconSet("social", 'img/icons/sets/social-icons.svg', 24);
})
.controller('BasicDemoCtrl', function DemoCtrl($mdDialog) {
...
Html:
<div class="md-menu-demo" ng-controller="BasicDemoCtrl as ctrl" ng-cloak>
...
My controller's name is MainController and I don't want to change my syntax.
My js syntax is:
app.controller("MainController", ["$scope","$http","$rootScope", function($scope,$http,$rootScope){
...
How can I change the code, in order to not to change my structure of my Controller and play?
Include $mdDialog like
app.controller("MainController", ["$scope","$http","$rootScope", "$mdDialog", function($scope,$http,$rootScope, $mdDialog){
... // order should be same.
And HTML
<div class="md-menu-demo" ng-controller="MainController" ng-cloak>
...
[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
Most articles demonstrate one method or the other... specifying the controller in the route OR the body. While I realize specifying the controller in the route provides additional benefits WHEN NEEDED (pre-loading required view resources, etc.), it is illogical to think that a modular application will be able to (or SHOULD) handle all of the functionality for a complex view.
Any proven examples (links) showing a combined approach would really help ease my mind.
Thanks, in advance.
No.
You may use both, but not for the same instance of a controller.
As an example, you can use ng-controller for your page menu controller and define the page controller loaded into your ui-view in the state config. This is fine as long as the page view does not have an ng-controller the same controller.
Your controllers constructor will run twice if you invoke it twice.
Added Snippet to Demonstrate
So in the following code we bring in the controller IndexCtrl in the uiRouter state config and in the index.html view. As you can see if you run this snippet, you have two instances of the IndexCtrl running.
Why is this a problem?
It is not a problem if this is your intention. It becomes a problem if this is your intention, to have two instances of the controller. If you follow this pattern unknowingly and create an application you will have an unneeded instance of every controller.
This will also cause developer confusion. How come I can't get back the value of a property in a controller? You will see bugs like this because a developer will set a property on one instance of a controller and then expect the same value to be in the other instance.
Controllers are classes. They are constructor functions -- so you can have multiple instances of them. And there are valid use cases to have multiple instances of a controller. But you shouldn't accidentally have two instances.
angular.module('app', ['ui.router'])
.config(['$stateProvider',
function($stateProvider) {
$stateProvider.state('index', {
url: '*path',
controller: "IndexCtrl as vm1",
templateUrl: 'index.html'
});
}
])
.controller('IndexCtrl', IndexCtrl);
function IndexCtrl() {
this.value = 0;
}
IndexCtrl.prototype.add = function() {
this.value += 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js"></script>
<div ng-app="app">
<ui-view></ui-view>
<script type="text/ng-template" id="index.html">
<div ng-controller="IndexCtrl as vm2">
Controller1 {{vm1.value}} Controller2 {{vm2.value}}
<button ng-click="vm1.add()">Add Ctrl1</button>
<button ng-click="vm2.add()">Add Ctrl2</button>
</div>
</script>
</div>
I am new to angularjs, and I have an jquery background.
I want to compile json from the server into an element with an template.
What I now have for so far is:
The template:
<script type="text/ng-template" id="/tiles.html">
<div ng-repeat="tile in tiles">
{{tile.name}}<img ng-src="tile.src" />
</div>
</script>
The button for displaying the content:
<button ng-click="imageOptions.addFromList()">+ Add Image from list</button>
The function:
$scope.imageOptions.addFromList = function (){
$http
.get('/json/Tiles/get')
.success(function(data){
$scope.tiles = data;
console.log(data);
})
.error(function(data){
console.log("something did go wrong");
});
$(".prompt").html('<div ng-include src="/tiles.html"></div>');
};
The placeholder:
<div class="prompt"></div>
The placeholder will be used many times with also other content.
So I can not just type the html from the .html() argument. Like this:
<div class="prompt"><div ng-include src="/tiles.html"></div></div>
When I inspect the .prompt div it will stay uncompiled
The first thing you should do is remove jQuery library from your app while you get familiar with angular methodology.
There is no need to use html() method when all you need to do is include your template through a variety of different ways in your html source.
If the data isn't already available for ng-repeat it will simply fail quietly and do nothing. Then when the data is available it will respond automatically.
You could simply do:
<div class="prompt" ng-include src="/tiles.html"></div>
Or you could make a simple directive that will accomplish the same thing .
app.directive('prompt', function() {
return {
restrict: 'C',/* use for "class" */
templateUrl: '/tiles.html'
}
});
Simply change this
<div ng-include src="/tiles.html">
to this
<div ng-include src="'/tiles.html'">
While coding your single page application in angularjs, ideally there should not be any need for you to first get a reference to an element and then perform some action on it (You may think of this as the first step of switching from a jquery background to angularjs domain).
To achieve complete separation of model, view and controller you should just define your templates and controllers accordingly. These mappings and references should be managed by angularjs on its own.
As correctly mentioned above you should not be using .html() method of jquery. If you have included jquery in your document, it will be internally used by angularjs, but, including jquery should not be mandatory for using angularjs.
ng-repeat and ng-include also create a separate scope, so you may want to take care of those as well in future.
For your query, you may reference the template by including extra quotes in ng-include as:
<div class="prompt">
<div ng-include src="'tiles.html'"></div>
</div>
http://jsfiddle.net/PKKp8/
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)