Angular component expression binding without template - angularjs

I'm trying to output my scope data from my component, but having a hard time figuring out how to do it without a local template.
For different reasons I need to have the markup ind the HTML file and not being parsed in with the js load
This is the dummy code so far: (codepen: http://codepen.io/anon/pen/qNBBRN)
HTML:
<comp>
{{ $ctrl.testing }}
</comp>
Non-working JS code:
angular
.module('Test', [])
.component('comp', {
controller: myCtrl,
});
function myCtrl() {
var model = this;
model.testing = '123';
}
document.addEventListener('DOMContentLoaded', function() {
angular.bootstrap(document, ['Test']);
});
And this is what I want to avoid even though it works:
angular
.module('Test', [])
.component('comp', {
controller: myCtrl,
template: '{{ $ctrl.testing }}',
});
function myCtrl() {
var model = this;
model.testing = '123';
}
document.addEventListener('DOMContentLoaded', function() {
angular.bootstrap(document, ['Test']);
});

A solution to what you need is using bindings to relate the component's inner private scope with the parent scope.
JS
angular
.module('Test', [])
.component('comp', {
controller: myCtrl,
bindings: {
testing: '='
}
});
function myCtrl() {
var model = this;
model.testing = '123';
}
HTML
<comp testing="$ctrl.testing">
{{ $ctrl.testing }}
</comp>
Plunkr example: http://plnkr.co/edit/jLeDyBTFA9OU7oqK5HSI?p=preview

Related

Angularjs 1.6.4: pass controller variable into component tags

I am newbie of angularjs, so might be i am doing something wrong. What i am trying to do, is to pass variable of another component controller into another component through tags. Below is the sample code.
post-view.template.html belongs to post-view component:
<ul class="list-group">
<li class="list-group-item">
<h4 class="list-group-item-heading">{{model.post.title}}</h4>
<p class="list-group-item-text">{{model.post.description}}</p>
</li>
</ul>
{{model.post.id}} <!-- getting value here -->
<rating-creator rating-type="1" parent-id="model.post.id"></rating-creator>
rating-creator.component.js:
(function () {
"use strict";
var module = angular.module(__appName);
function controller() {
var model = this;
model.$onInit = function () {
//getting undefined here. Why?
console.log("parent:"+model.parentId);
};
}
module.component("ratingCreator", {
templateUrl: "components/rating-creator/rating-creator.template.html",
bindings: {
ratingType: "<",
parentId: "<"
},
controllerAs: "model",
controller: [controller]
});
}());
The issue is most likely that model.post.id is not available when the component is being instantiated. Try using ng-if to only generate an instance of the component when model.post.id has a value other than undefined. It would look like this:
Controller
app.controller('MainCtrl', function($scope, $timeout) {
var model = this;
// simulating model.post.id not being available on page load
$timeout(function() {
model.post = {
id: 123
};
}, 1000);
});
HTML
<rating-creator ng-if="model.post.id" rating-type="1" parent-id="model.post.id"></rating-creator>
Here is a plunker demonstrating the functionality. It uses a $timeout to simulate the model.post.id being populated async.
Hopefully that helps!

How to include JSON object in AngularJS without writing JavaScript?

I have a JSON file with this object:
{"software":[
{"name":"Eclipse", "version":"4.5"},
{"name":"Sublime Text", "version":"3.0"},
{"name":"ConEmu", "version":"1.5"}
]}
I want to get the values using the AngularJS built-in directives, I tried with ng-include and ng-repeat, but doesn't work:
<div ng-include="'software.JSON'" ng-repeat="sw in software">{{sw.name}} v{{sw.version}}</div>
Demo app:
var MyApp = angular.module("MyApp",[]); // Inject app dependencies here
Declare service to fetch JSON file:
MyApp.factory("ConstantsService", ["$http", function($http){
var ConstantsService = {};
ConstantsService.getConstants = function(){
return $http({
method: "GET",
url: "constants.json", //JSON file location
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data){
return data;
});
}
return ConstantsService;
}]);
Inject ConstantsService into your controller and access JSON content:
MyApp.controller('FetchJsonController', ['$scope', 'ConstantsService', function($scope, ConstantsService){
ConstantsService.getConstants().then(function(response){
console.log(response.data.software); //Software object declared in JSON file
$scope.software = response.data.software;
});
}]);
Finally define template:
<div ng-controller="FetchJsonController">
<div ng-repeat="sw in software">{{sw.name}} v{{sw.version}}</div>
</div>
ng-include directive is not made for this purpose.
ngInclude
directive in module ng
Fetches, compiles and includes an external HTML fragment.
Try achieving it with an $http.get in your controller. But if you really need a directive for it...
http://jsfiddle.net/U3pVM/22528/
JS
(function() {
var app = angular.module('app', []);
app.controller("IncludeJsonController", IncludeJsonController);
app.directive("includeJson",[function() {
return {
template: "<div ng-repeat=\"sw in vm.software\"><p>{{ sw.name }} v{{ sw.version}}</p></div>",
controller: "IncludeJsonController",
restrict: "E",
link: function(scope, element, attr, ctrl) {
scope.vm = {};
scope.vm.filename = attr.filename;
scope.vm.software = ctrl.getSoftwares();
}
}}]);
IncludeJsonController.$inject = ['$scope', '$http'];
function IncludeJsonController($scope, $http) {
var self = this;
self.getSoftwares = getSoftwares;
function getSoftwares() {
//in your case, use $http.get('path/to' + $scope.vm.filename);
return [
{"name":"Eclipse", "version":"4.5"},
{"name":"Sublime Text", "version":"3.0"},
{"name":"ConEmu", "version":"1.5"}
];
}
}
}());
HTML
<div ng-app="app">
<include-json filename="software.json"></include-json>
</div>

how to use angular 1 variables inside the angular 2 component

login.js
var app= angular.module('login',[]);
app.controller('LoginCtrl', function($scope)
{
$scope.sayhello="Hello"+ $scope.username;
}).directive('loginDir', function(){
return {
scope:{},
templateUrl: 'logintpl.html',
controller: 'LoginCtrl'
};
});
var adapter = new ng.upgrade.UpgradeAdapter();
AppComponent = ng.core
.Component({
selector: 'login',
directives: [adapter.upgradeNg1Component('loginDir')],
template: '<login-dir></login-dir>'
})
.Class({
constructor: function() {}
});
app.directive('login', adapter.downgradeNg2Component(AppComponent));
document.addEventListener('DOMContentLoaded', function() {
adapter.bootstrap(document.body, ['login']);
console.log(adapter);
});
logintpl.html
<input type="name" ng-model="username">
how can i use $scope.sayhello variable inside the component.
eg: component template should be,template:'<login-dir></login-dir>{{sayhello}}
AppComponent = ng.core
.Component({
selector: 'login',
directives: [adapter.upgradeNg1Component('loginDir')],
template: '<login-dir></login-dir> {{sayhello}}'
})
.Class({
constructor: function() {
this.sayhello = "Hello World !!!";
}
});
Explanation
In Angular 2, there is no model called $scope. They replaced it with simple variables with in the Class.
We can consider the whole Class as a controller in Angular 1.x. We can create variable with this.variable_name with in a Class. constructor is the function which will be invoked first in the component. So, we can initialize all our variable here.
So, $scope.variable_name in Angular 1.x is same (or likely to) as this.variable_name in Angular 2.
In fact the sayhello attribute can be only used within your loginDir directive since it's defined in its associated scope.
You could have a use case like that:
app.controller('LoginCtrl', function($scope) {
$scope.sayhello = function() {
console.log("Hello"+ $scope.username);
}
}).directive('loginDir', function() {
return {
scope:{},
templateUrl: 'logintpl.html',
controller: 'LoginCtrl'
};
});
In the template of your Angular1 directive, you will be able to use this function:
<input type="name" ng-model="username">
<span ng-click="sayhello()">Test</span>
I don't know what you exactly want to do. Here is the corresponding plunkr: https://plnkr.co/edit/ribHwk8uSXHRv0JkLlo0?p=preview.
Edit
You can have access to attributes of the parent component from the directive since the Angular1 directive scope is internal to your directive. With Angular1, you can't either. The only thing you could do is to define a parameter to your Angular1 directive that corresponds to an attribute of your parent component and update it by reference.
Here is a sample
app.controller('LoginCtrl', function($scope) {
$scope.updateSayhelloInParent = function() {
console.log("Hello"+ $scope.username);
$scope.sayhello.message = $scope.username;
}
}).directive('loginDir', function(){
return {
scope:{
sayhello: '='
},
templateUrl: 'logintpl.html',
controller: 'LoginCtrl'
};
});
And the way to use the directive in the component:
AppComponent = ng.core
.Component({
selector: 'login',
directives: [adapter.upgradeNg1Component('loginDir')],
template: `
<login-dir [sayhello]="sayHello"></login-dir>
<br/><br/>
SayHello in component:
{{sayHello | json}}
`
})
.Class({
constructor: function() {
this.sayHello = {
message: 'default message'
}
}
});
Corresponding plunkr is here: https://plnkr.co/edit/ribHwk8uSXHRv0JkLlo0?p=preview.
Hope it helps you,
Thierry

AngularJS Directive accessing controler scope

TL;DR I solved my problem. Here is plunker with 3 different solutions:
http://plnkr.co/edit/E0ErKs?p=preview
I don`t like slider1 because it stores value in $scope ( {{sliderValue}} ) and according to recommendation from Angular Style Guide we should avoid that.
I don`t like slider2 because it assumes that controler have alias vm in a view (so we create some kind of coupling between view and directive).
Solution 3 looks OK for me. Am I missing something?
How would you write differently this directive to be in complience with Angular philosophy?
INITIAL QUESTION:
I am learning angular and not everything is clear to me yet.
I found this question:
How to use jQuery in AngularJS
So I created working example:
Directive:
(function() {
'use strict';
angular.module('demoApp').directive('slider', function () {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
$scope.onSlide = function (e, ui) {
$scope.sliderValue = ui.value;
$scope.$digest();
};
},
link: function (scope, el, attrs) {
var options = {
value: scope.sliderValue,
slide: scope.onSlide
};
// set up slider on load
angular.element(document).ready(function () {
scope.$slider = $(el).slider(options);
});
}
}
});
})();
Controller:
(function() {
'use strict';
angular.module('demoApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
DemoAppTestCtrl.$inject = [ '$scope' ];
function DemoAppTestCtrl($scope) {
$scope.sliderValue = 10;
}
})();
And Html page:
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{sliderValue}}
<div slider></div>
</div>
Everything works fine. Angular put slider in place of <div slider> and I can move it and I see changing values in {{sliderValue}}.
Then I found this Angular Style Guide
https://github.com/johnpapa/angular-styleguide
In chapter about controllers they recommend to use controllerAs with vm syntax (because $scope is bad or something).
Ex:
function CustomerController() {
var vm = this;
vm.name = {};
vm.sendMessage = function() { };
}
So I changed my controller to this:
(function() {
'use strict';
angular.module('demoApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
DemoAppTestCtrl.$inject = [ ];
function DemoAppTestCtrl($scope) {
var vm = this;
vm.sliderValue = 10;
}
})();
And Html page to:
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{vm.sliderValue}}
<div slider></div>
</div>
But i don`t know how to fix my directive.
I want the same functionality, when i move the slider i want to set vm.sliderValue inside controler instead $scope.sliderValue inside scope.
EDIT1:
I was able to make it work by adding $scope.vm inside controller and link functions (because my controller sits in scope as vm). But I am not sure if this is right way to do it, because now my directive assume that there is controller in scope under $scope.vm alias.
Is this bad design or normal way of doing things in Angular ?
(function () {
'use strict';
angular
.module('demoApp')
.directive('slider', slider);
slider.$inject = [ ];
function slider() {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
$scope.vm.onSlide = function (e, ui) {
$scope.vm.sliderValue = ui.value;
$scope.$digest();
};
},
link: function (scope, el, attrs) {
var options = {
value: scope.vm.sliderValue,
slide: scope.vm.onSlide
};
// set up slider on load
angular.element(document).ready(function () {
scope.$slider = $(el).slider(options);
});
}
}
}
})();
EDIT2:
I was able to create working Plunker with 3 different versions:
http://plnkr.co/edit/E0ErKs?p=preview
Use scope: false as a option in the directive.
http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/
try something like this:
angular.module('myApp', []);
angular.module('myApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
function DemoAppTestCtrl() {
var vm = this;
vm.sliderValue = 10;
}
angular.module('myApp').directive('slider', sliderDirective );
function sliderDirective() {
return {
restrict: 'A',
controller: sliderController,
controllerAs: 'sliderCtrl',
template: "<p>{{sliderCtrl.test}}</p>"
}
}
function sliderController() {
var vm = this;
vm.test = "hello";
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{vm.sliderValue}}
<div slider></div>
</div>
</div>

Angular variable manipulation from different directives

I am a bit struggling with infinite scroll in angular. I have one object array where all items are stored in. This object is part of directive controller.
Now when I am trying to implement infinite scroll I use separate directive to calculate offsets. I would like to access from this scroll directive variable from the other directive where object array is defined.
How can I do this? What would be the easiest way here? I am searching for week and can't find anything easy enough to implement to my solution.
Thank you
You could either use the directive's require property to get the $scope of another directive's controller, or use the parent controller of the directives to pass in a shared value. Here's an example using require (live demo).
<div ng-app="myApp">
<foo></foo>
<bar></bar>
</div>
angular.module('myApp', [])
.directive('foo', function() {
return {
controller: function($scope) {
$scope.foo = 123;
}
};
})
.directive('bar', function() {
return {
require: '^foo',
controller: function($scope) {
console.log($scope.foo);
}
};
})
;
The timing for this next example may not be what you want. They are sharing the same variable, but changes to $scope in the first directive's controller won't be applied until after the second directive's controller has already run. (live demo).
<div ng-app="myApp" ng-controller="MyCtrl">
{{sharedValue}}
<foo shared-value="sharedValue"></foo>
<bar shared-value="sharedValue"></bar>
</div>
angular.module('myApp', [])
.controller('MyCtrl', function($scope) {
$scope.sharedValue = 'abc';
})
.directive('foo', function() {
return {
scope: {
sharedValue: "="
},
controller: function($scope) {
console.log($scope.sharedValue); // abc
$scope.sharedValue = 123;
}
};
})
.directive('bar', function() {
return {
scope: {
sharedValue: '='
},
controller: function($scope) {
console.log($scope.sharedValue); // still abc, will update later
}
};
})
;

Resources