Is there an equivalent in Angular JS of props from React JS? - angularjs

I am learning angularJS and I was wondering if there is the equivalent of props like there is in react. More specifically, a way to add with props that I define in another file. It would make the website I'm coding in angular much more efficient but I can't find the equivalent in Angular.

There is a way to do something similar in AngularJS. It is called Directives. In your case you want to create a directive with restrict set to 'E'.
'E' tells the compiler that it will be an element in the generated HTML.
angular.module('scopeDirective', [])
.controller('app', ['$scope', function($scope) {
$scope.naomi = { name: 'Uriel', address: '1600 Amphitheatre' };
$scope.igor = { name: 'Bitton', address: '123 Somewhere' };
}])
.directive('customer', function() {
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
templateUrl: 'customer.html'
};
});
The scope object after restrict defines the various attributes you would want this directive to accept. This is similar to how props work in React. In that object, you can see customerInfo which corresponds to the directive's isolate scope property. The value (=info) tells $compile to bind to the info attribute.
The templateUrl maps to the HTML for this directive.
Using the above directive will look something like below:
<div ng-controller="app">
<customer info="uriel"></customer>
<hr>
<customer info="bitton"></customer>
</div>
Kindly refer to the AngularJS docs on Directives.
NOTE: Instead of trying to do something in AngularJS that is similar to how you do things in React or any other framework/library, I would suggest you do not instead embrace the current framework's capability and use it as is without trying to compare way of achieving similar things as this can lead to frustration down the road.
I hope you find this answer useful for your needs.

Is it not the same as using #Input() info:string in the component and when its being used
<component [info] = "string">
?
in react it would be {props.info} in the component
and then <component info="something"> when its being used

Related

Angular directive from array element in ngRepeat

I am working on a project that will have any number of HTML cards, and the format and positioning of the cards is the same, but the content of them will differ.
My plan was to implement the content of each card as an angular directive, and then using ngRepeat, loop through my array of directives which can be in any order, displaying each directive in a card. It would be something like this:
inside my controller:
$scope.cards = [
'directice-one',
'directive-two',
//etc..
]
my directive:
.directive('directiveOne', function () {
return {
restrict: "C",
template: '<h1>One!!</h1>'
};
})
.directive('directiveTwo', function () {
return {
restrict: "C",
template: '<h1>Two!!</h1>'
};
})
//etc..
my html:
<div class="card" ng-repeat="item in cards">
<div class="{{item}}"></div>
</div>
But.. the directives don't render. So I just get a div with class="directive-one".
This might be a design flaw on my part, some sort of syntax error, or just a limitation of angular. I'm not sure.
I've also considered making a directive <card>, and then passing the templateUrl: into it, but that would cause me to lose my access to $scope and the javsacript capabilities that I would have if each card was it's own directive.
So, advise, code help, anything would be very helpful!
I choose directives only when I need to use them in HTML mark up. For example, assuming cards layout is same and it takes different information based on user preference.
HTML File
<my-card Name="First" Option="Myoptions"></Card>
<my-card Name="Second" Option="GenOptions"></Card>
Directive
angular.module("testapp").directive("MyCard", function() {
scope: {
name: '#',
Option: '#'
Controller: "myCardController",
templateURL: "~/myCard/myCardTemplate.html"
}
});
In Template you can implement the information passed from HTML page via the directive.
Hope this helps.
Do take note that the above approach is preferred when you are developing a framework sort of things. For example you develop a web framework and the header takes 5 parameters and these 5 parameters needs to be passed via mark up. Most important thing is that the framework/header is independent
In your controller, you need to require the directive modules. Then assign them to a scope variable which would be that array you have. Will update with code when I get to desktop, tried doing with phone kinda tuff.

Custom search directive

I am building a small search app using AngularJS and Elasticsearch. I'm using AngularJS UI Bootstrap Typeahead for autocomplete functionality. Now I'm trying to create a custom search directive for the search functionality. Still learning AngularJS directives...
I should just be able to add the UI Bootstrap Typeahead directive to this custom search directive, right? (as an attr).
So I would just need to pass the suggestion function, search function and search terms (ng-model) to my custom search directive?
Using examples and citing from Angular Developer Guide: Directives.
Q1: "I should just be able to add the UI Bootstrap Typeahead directive to this custom search directive, right? (as an attr)."
A1: As your custom directives are dependency injected, you should be able to use any Angular component that you would normally dependency inject:
"Just like the module.controller API, the function argument in module.directive is dependency injected. Because of this, we can use $interval and dateFilter inside our directive's link function."
angular.module('docsTimeDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
}])
.directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) {
Q2: "So I would just need to pass the suggestion function, search function and search terms (ng-model) to my custom search directive?"
A2: That is one way to do it, however your directive would be dependent on the controller host to have the functionality. If you go with this route you would do so with the & operator.
I would however go with the link property. Here your directive can have the code needed for calculations, and you can inject the search parameters into it with the = operator.
Link example of mine. I think you should be able to convert it to your problem easily :)
Directive:
function statisticsTableDirective(common) {
return {
restrict: 'E',
scope: {
tabledata: '='
},
templateUrl: 'app/statistics/statisticsTable.html',
link: function (scope, element, attrs) {
var vm = scope;
vm.isLastMonth = isLastMonth;
function isLastMonth(index) {
return index+1 === new Date().getMonth();
}
}
};
}
In the statisticsTable.html I can now use isLastMonth as it were directly on the scope. Like in a simple ng-class:
ng-class="::{highlight : isLastMonth($index)}"></td>

Multiple use of a template in the same page in AngularJS

I have some repetitive components on my AngularJS page such as billingAddress, shippingAddress and primaryAddress. I have created a separate template file for address components and expected to be able to use ng-include to include the template thrice on my page. I am unable to find documentation around passing models to templates. What I am looking for is something like
<div ng-include="address.tpl.html" ng-model="{address: primaryAddress}"></div>
<div ng-include="address.tpl.html" ng-model="{address: billingAddress}"></div>
<div ng-include="address.tpl.html" ng-model="{address: shippingAddress}"></div>
Is this even possible as of now?
his is for what directives are made.
angular.module('docsSimpleDirective', [])
.directive('myAddress', function() {
return {
scope: {
address : '='
},
templateUrl: 'address.tpl.html'
};
});
Then in your template simple use the $scope.address.
On declaring the directive you should use it like this.
<my-address address="primaryAddress"><my-address>
<my-address address="billingAddress"><my-address>
<my-address address="shippingAddress"><my-address>

How to write re-usable HTML components with AngularJS

New to Angular and, so far, I'm loving it but the learning curve seems pretty steep. What I want to do is wrap up a bit of simple business logic and build some re-usable DOM components to template common areas of my system. Specifically I am writing a simple survey application that has different question types. My goal is to get to the point that while I am in an ng-repeat block I can do something like this:
<div ng-repeat="question in questions">
<show-question></show-question>
</div>
Ideally I want to wrap all of the logic into that one statement to switch on question type and then pull from templateUrl for different HTML sets. So if a question.type = "text" it would pull the templateUrl of "templates/textQuestion.html" and be able to inject scope into that template file as it produces the DOM element.
Big question is, am I going about this correctly AT ALL? Is a directive the way to go, should I even try to do this all in one directive/tag? I am open to being schooled on this!
Small question is, if I am going the right direction, what is the correct implementation?
I have already tried putting some logic inside my directives like IF and SWITCH, but that doesn't appear to be valid.
Any and all (constructive) help is welcome.
Thanks all!
It's called a directive. There's a complete guide here: http://docs.angularjs.org/guide/directive
It allows you to make custom attributes, elements, CSS classes, and comments that turn into components.
I wouldn't pull separate templates for each question type, I'd use a different directive for each question type. Then you can switch between them using a parent directive.
Here is what a directive that loads different directives might look like:
app.directive('question', function($compile){
"use strict";
return{
restrict: 'E',
replace: true,
link: function(scope, element, attrs){
var render = function(){
var template = "";
switch(attrs.type){
case "truefalse":
template = '<truefalse></truefalse>';
break;
case "multiplechoice":
template = '<multiplechoice></multiplechoice>';
break;
case "essay":
template = '<essay></essay>';
break;
}
element.html(template);
$compile(element.contents())(scope);
}
attrs.$observe('type', function(value) {
render();
});
render();
}
};
});
Now you could use this as such:
<question ng-repeat="question in questions" type="question.type" ></question>
Assuming you had a directive for each type of question, you'd get different directives rendered. This is sort of similar to using ng-if or different templates or whatever but I like it more because I also get re-usable one-off components.
So if your scope variable questions has all the info for each question like
$scope.questions = [
{ type: 'input',
prompt: 'name'
}
];
Then you might have some html that looks like
<div question="question" ng-repeat="question in questions"></div>
And have a directive that looks something like
app.directive('question', function() {
return {
scope: {
question: '=' // Creates 2 way data binding with the object you passed in as attribute by the same name
},
link: function($scope, $element, $attrs) {
$scope.question; // This will be equal the object you passed in
// { type: 'input', prompt: 'name' }
// You can modify the dom or whatever here
}
};
});
If you want to have different prepared templates then you can inject the $templateCache into your directive
app.directive('question', function($templateCache) {
and then call them in your link function
link: function($scope, $element, $attrs) {
var template = $templateCache.get('path/to/template.html');
// and append it to the element
$element.append(template);
}
You'll have to play around with it a bit, but that's half the fun. Good luck!

AngularJS DOM Access On Init

I wrote this simple jsfiddle in which I do (successfully) some basic addClass on a directive in a ng-repeat.
http://jsfiddle.net/rv6u2/5/
Now, my question is: which is the best (or intended) place to do such DOM manipulations:
A. In the directive?
B. In the controller?
Both possibilities are shown in my example.
Code:
var TestApp = angular.module("TestApp", ['ngResource']);
TestApp.directive('onLoad', function() {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
elm.addClass('loaded'); // A: DOM manipulation in directive
scope.initMe(scope.$eval(attrs.onLoad2), elm); // B: DOM manipulation handled in controller
}
};
});
thanks in advance :)
NEVER manipulate the dom inside of controllers.
Controllers should just use services and update attributes of $scope. All DOM manipulation should be made by directives and(in some cases) services(e.g. $anchorScroll)
See the concepts of angularjs here
UPDATE: Example of the correct way here
A more "Angular way" of setting class loaded2 would be as follows (which avoids DOM manipulation inside the controller):
In the HTML, declare a model for the class (myClass):
<div ng-repeat="item in items" ng-model="item" on-load="initMe(item)" ng-class="myClass">
In the link function, just call the controller method:
scope.initMe()
In the controller, manipulate the model/$scope property:
$scope.initMe = function() {
$scope.myClass = "loaded2";
}
Changing the model/scope will automatically update the view.
This method is useful if you want to declare in the HTML that a class is being controlled by $scope property myClass. However, using elm.addClass() inside the linking function is more self-contained and easier to maintain (and I like that approach better).

Resources