Set $rootScope before textAngular directive initial - javascript

I wanna custom textAngular directive options.
The API document says I should set $rootScope.textAngularTools.{customButton} to build a function.
But if I set on controller, directive will tell me property is undefined.
If I set in module.run function, the $rootScope.textAngularTools is undefined.
How do I set the option before directive initialize ?
<text-angular to-toolbar="[['customButton']]">
Setting like this (coffeescript)
$rootScope.textAngularTools.colourRed =
display: "<button ng-click='action()' ng-class='displayActiveToolClass(active)'><i class='fa fa-square' style='color: red;'></i></button>",
action: ->
console.log 'action'
activeState: ->
false

from reading the sources i would suggest you do your configuration in a module run function:
angular.modul('myApp', ['textAngular'])
.run(function($rootScope){
$rootScope.textAngularTools = {
colourRed: {
display: "<button ng-click='action()' ng-class='displayActiveToolClass(active)'><i class='fa fa-square' style='color: red;'></i></button>",
action: function(){
console.log('action);
}
}
};
})
.controller('yourController')...
why should this work? They extends an existing textAngularTools object in their directive:
$rootScope.textAngularTools != null)? $rootScope.textAngularTools : {}

Related

angular class and ng-class

So in Angular you can do this:
ng-class="{ 'my-class': widget.widgetId == '1'}
Which will give the element the class of my-class if the widget.widgetId equals 1.
You can also do this:
class="col-md-{{widget.size}}"
Which will give the element a class of col-md- and then returns whatever widget.size is set to.
However what if I want to do what I'm doing in the second example but with ng-class. For example the following:
ng-class="{ 'col-md-{{widget.size}}': widget.widgetId == '1'}"
I've tried the above with various syntax but it does not seem to work. Is this possible with ng-class?
Here is a basic example of using ng-class:
HTML:
<button ng-class="getLanguageOptionClass(lan)" ng-repeat="lan in language.available" ng-click="language.current=lan">{{lan}}</button>
Javascript
var I18nController = function($scope){
$scope.language = {
current: 'no',
available: ['en', 'no', 'sv', 'da', 'fi']
};
$scope.getLanguageOptionClass = function(language){
return ($scope.language.current==language) ? 'btn btn-primary' : 'btn btn-info';
}
};
Description : getLanguageOptionClass gets called with lan as a parameter, which returns a value which is assigned as a class.
Working example
Not sure wether above will work or not but you can achieve this using by defining a function inside your controller as,
$scope.getClass= function(widget){
var baseClass = "col-md-";
if(widget.widgetId==='1'){
return baseClass + widget.size;
}
}
Inside your html use it like,
ng-class="getClass(widget)"
define a function in controller to get class name processed
$scope.getClassName = function(widgetSize){
return 'col-md-' + widgetSize;
}
call this function from ng-class
ng-class="getClassName(widget.size)"

Unable to set the value of bound property in angular component

I'm trying to implement a bound property for an angular component as explained in the component documentation and this example.
Unfortunately the values I'm assigning at the tag level or in the $onInit methods are never used. Nor is the value printed when I use it as a model value.
You can find the full code on plunker.
My binding definition:
(function(angular) {
'use strict';
function SearchResultController($scope, $element, $attrs) {
var ctrl = this;
ctrl.searchFor = 'nohting-ctor';
ctrl.$onInit = function() {
console.log('SearchResultController.$onInit: searchFor='+ctrl.searchFor);
ctrl.searchFor = 'nothing-int';
};
}
angular.module('myApp').component('searchResult', {
templateUrl: 'searchResult.html',
controller: SearchResultController,
bindings: {
searchFor: '<'
}
});
})(window.angular);
Template:
<p>SearchResult for <span ng-model="$ctrl.searchFor"</span></span></p>
How it's used:
<h1>Main Window</h1>
<search-input on-start-search="$ctrl.startSearch(value)"></search-input>
<search-result search-for="nothing-ext"></search-result>
None of the nothing-* values is evers shown.
Any ideas what's wrong?
The usage of you component is not correct. If you want to pass a string it should be quoted:
<search-result search-for="'nothing-ext'"></search-result>
Then next problem is that this line
<p>SearchResult for <span ng-model="$ctrl.searchFor"</span></span></p>
doesn't make sense, as ngModel directive is only valid for input controls. You want ngBind or simple {{ $ctrl.searchFor }}:
<p>SearchResult for <span ng-bind="$ctrl.searchFor"</span></span></p>

Model's attribute not getting updated on change

I need to bind status message to variable. If it's empty I need to hide div. If it's not empty show div and message. At the moment when I update $scope.statusMessage.msgin controller the {{statusMessage.msg}} is not being updated. How to fix it?
<div class="alert alert-info"
ng-class="{'alert-danger': statusMessage.status == -1, 'alert-success': statusMessage.status == 1, 'alert-warning': statusMessage.status == 0}"
role="alert" ng-hide="(statusMessage.msg).length == 0">{{statusMessage.msg}}</div>
$scope.statusButtonAction = function(action){
$scope.statusMessage.msg = 'Validation Error';
}
UPDATE 1
<form role="form" class="form-inline" id="status-buttons">
<button type="button" class="btn btn-warning btn-lg" ng-click="statusButtonAction('Validation Error')">Validation Error</button>
</form>
UPDATE 2
declaration of :
$scope.statusMessage = {
msg: '',
status: null
};
UPDATE 3
http://jsbin.com/jaquc/2/
UPDATE 4
I use https://github.com/angular-ui/ui-router . and my config is :
prototypeApplication.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider.state('welcome', {
url: '/',
views: {
'': {
templateUrl: 'views/prototype/welcome.html',
controller: 'welcomeController'
},
'header#welcome': {
templateUrl: 'views/components/header.html'
},
'check-in#welcome': {
templateUrl: 'views/prototype/welcome/check-in.html',
controller: 'checkInController'
},
'status-buttons#welcome': {
templateUrl: 'views/prototype/welcome/status-buttons.html',
controller: 'checkInController'
}
}
})
UPDATE 5
Strange enough, but when I switch from object oriented representation to plain variables all works:
$scope.statusMessage = '123';
$scope.statusState = null;
Does it mean I have to use some trick to make AngularJS read object's property's values?
Make sure that you defined $scope.statusMessage ie:
$scope.statusMessage = {};
before you try set $scope.statusMessage.msg in your statusButtonAction function.
and use
ng-hide="!statusMessage.msg" or ng-show="statusMessage.msg"
instead
ng-hide="(statusMessage.msg).length == 0"
Please see working demo here http://jsbin.com/vavahi/1/edit
I think the issue is use of '==' instead of '==='. The former does type coercion and results are a bit 'unreliable' especially with strings/arrays... At least I use ng-hide="myArray.length === 0" as the way to control and works always as it should.
(However, I am not sure what you are trying to do in your ng-class with "... == -1" etc. - at least I don't see statusMessage.status being declared nor used in your code...)
I found that editing ui-router configuration like this solves the problem
...
check-in#welcome': {
templateUrl: 'views/prototype/welcome/check-in.html'
/*,
controller: 'checkInController' */ // should be removed to work!
...

Angular dynamic nested directive, passing a new scope to each child?

I'm trying to create a nested directive in angular, which is specified by a large, possibly deeply nested config object. I'm having trouble figuring out how to get the correct scope to the correct directive.
My example plunkr is here: http://plnkr.co/edit/AJVaSk2GSxIJvZx3B6UX?p=preview
I think that I could copy the current child config into an attribute on each child and get that via the scope attribute of the directive definition, but that seems a little crazy - it could be huge, and might need to be escaped.
Is there a better way to do this, or something else I've completely missed? (Or another SO question that answers this that I've missed...)
Very much obliged.
Edit from comment for clarification
I'm trying to get each directive to have a scope (or even just a variable on the scope?) for each sub-object. So 'theform' directive gets the top level object, each 'theforma' directive gets the child that created it. That is, I'd have one 'theforma' directive with the object containing 'type' : 'form_a_child_1', and the other would have the object containing 'type' : 'form_a_child_2'. So they'd be "self-contained" and only really know about the object they were created with.
Ultimately, then, I'd like a submit button, and a way to gather all of the information from the dynamic form elements...but that's for another question.
Got distracted by video games yesterday hope this wasn't terribly urgent to get resolved but I posted an answer here now. This is probably not exactly what you want to do but should get the idea across about isolate scope.
http://plnkr.co/edit/FmpixQrBmvWJlB2wBRXW?p=preview
JS
// Code goes here
var app = angular.module('formtest', []);
app.controller("MyCtrl", function($scope){
$scope.form_config = {
// first level children are the tabs
children : [
{
type : 'form_a',
label : 'Form A',
children : [
{
type : 'form_a_child_1',
label : 'Form A Child 1',
},
{
type : 'form_a_child_2',
label : 'Form A Child 2',
},
]
},
{
type : 'form_b',
label : 'Form B',
children : [
{
type : 'form_b_child_1',
label : 'Form B Child 1',
},
{
type : 'form_b_child_2',
label : 'Form B Child 2',
},
]
},
]
};
})
/**
* Should display the top level form info.
* Form A and Form B
*/
app.directive('theform', function() {
return {
restrict : 'E',
scope:{formConfig:"="},
template : '<div>'+
'<div>'+
'<div><theforma form-a="formAData"></div>'+
'<div><theformb form-b="formBData"></div>'+
'</div>'+
'</div>',
replace : true,
link : function(scope, $el, $attrs) {
//$scope.form_config = form_config;
scope.formAData = [];
scope.formBData = [];
scope.$watch("formConfig", function(newVal){
if(newVal)
{
for(var i=0; i<scope.formConfig.children.length; i++)
{
var curElement = scope.formConfig.children[i];
console.log(curElement);
if(curElement.type == "form_a")
scope.formAData = curElement;
else
scope.formBData = curElement;
}
}
console.log(scope.formAData);
})
}
};
});
/**
* I want to display the children of Form A
*/
app.directive('theforma', function() {
return {
restrict : 'E',
scope : {formA : "="},
template : '<div>'+
'<div ng-repeat="child in formA.children">'+
'Form A: {{child.label}}'+
'</div>' +
'</div>',
replace : true,
link : function(scope, $el, $attrs) {
//console.log(scope.formA)
}
};
});
/**
* I want to display the children of Form B
*/
app.directive('theformb', function() {
return {
restrict : 'E',
scope : {formB : "="},
template : '<div>'+
'<div ng-repeat="child in formB.children">'+
'Form B: {{child.label}}'+
'</div>' +
'</div>',
replace : true,
link : function(scope, $el, $attrs) {
scope.children = scope.formB;
}
};
});
HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.2.14" src="http://code.angularjs.org/1.2.14/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app='formtest' ng-controller="MyCtrl">
<div>
<theform form-config="form_config"></theform>
</div>
</body>
</html>
When it comes to isolate scope for a directive you have three options for what you put in the quotes after a property name (=, &, or #). = makes the property two way bind, so if you pass in a Javascript object the changes are made to that object, # will get you a string result from the expression you pass in and pass through that string, & allows you to pass a reference to a function to be executed on the original scope (that of the controller or another directive that uses it).

AngularJS ng-model in template passed to directive controller

I've got a directive with a controller, that builds a form for posting comments to an API via CommentsService
My directive looks a bit lik this:
app.directive('appComments', function( CommentService ) {
return {
restrict: 'E',
scope: {
event: '='
},
controller: function( $rootScope, $scope, $element ) {
$scope.comments = [];
$scope.comment_text = '';
// load comments if event ID has changed
$scope.$watch( 'event', function() {
if( typeof $scope.event != 'undefined' ) {
CommentService.get( $scope.event ).then(
function( comments ) {
$scope.comments = comments;
}
);
}
});
// post comment to service
$scope.postComment = function() {
if( $scope.comment_text != '' ) {
CommentService.post(
$scope.event,
$scope.comment_text,
function() {
// code to reload comments
}
);
}
};
},
templateUrl: '/partials/comments.html'
};
});
This is my comments.html for the directive
<div class="event-comments">
<p ng-if="!comments.length">
<span>This event has no comments.</span>
</p>
<div
class="event-comment"
ng-repeat="comment in comments"
>
<div class="comment-text">{{comment.text}}</div>
</div>
</div>
<div class="insert-comment-container" ng-if="!loading">
<form ng-submit="postComment()">
<textarea
ng-model="comment_text"
></textarea>
<div
ng-tap="postComment()"
>Post</div>
</div>
</div>
This is how I'm placing it in my main view:
<app-comments event="event.id"></app-comments>
My comments are loading, and the event id is getting passed, but when I try and post a comment the comment_text is blank.
I think I'm getting my scopes mixed up or something, I'm still not completely clear on directives
** update **
I've just realised if I set
$scope.comment_text = 'Initial text'
in the directive, it appears when the template renders inside the textarea, and the if statement in the postComments() function fires. But if I change the text in the textarea, and tap the post button, the contents of $scope.comment_text is still "Initial text", it seems to be a one way binding.
Since you are using form i believe it creates a new scope scope. As per documentation
If the name attribute is specified, the form controller is published
onto the current scope under this name.
Try to give your form a name. Or else try to pass the property as object property like
<textarea
ng-model="comment.comment_text">
</textarea>
Ok so by changing comment_text to comment.text it solved the problem, as recommended by this SO answer:
angular-bootstrap (tabs): data binding works only one-way
Using an object instead of a primitive just uses the same reference to the object property, instead of copying the primitive into the new scope.

Categories

Resources