I want to create a chat Application with Angular.js. messages stored inside an Array and i have to bind that array to the HTML view. if i click on every message, i need to an alert() fired inside the controller. my controller is like this:
app.controller("timelineCtrl", function ($scope) {
var self = this;
self.arr={};
// this called from a socket message and pushed it's data to this array
self.arr.push(socket_message);
}
I have this directive:
app.directive('helloWorld', function() {
return {
restrict: 'AE',
scope:{
post: '='
},
template: '<div class="timeline-item"> Hello {{postCtrl.post}} </div>',
controller: 'timelineCtrl',
controllerAs: 'postCtrl',
bindToController: true
};
});
and this is my HTML view:
<div ng-controller="timelineCtrl as postCtrl">
<div ng-repeat="post in postCtrl.arr">
<hello-world post='post'></hello-world>
</div>
</div>
right now, with this structure, when first item goes to array everything is OK, when second item came, doesn't append to array and replaces with first item, then next items behave normal and append to array. why second item replace with first item?
This is my array log in browser console:
It seems this problem related to controller aliasing section inside directive because when i add bindToController and controllerAs: 'postCtrl', this strange behavior appears. first time i press the button HTML updates normally, but next times nothing works. i log that array and it seems when second item arrives, replaces with first item for no reason. it seems an overlapping issue or something like that.
Related
Basic Setup:
I have a modal form that has a custom directive (child element) inside of it. The custom directive is a button. The modal consists of an input checkbox as well.
The End Goal:
Whenever the checkbox is 'checked', the custom directive/button element should be enabled. If the checkbox is 'unchecked', the custom directive/button should be disabled.
What I have so far AKA my thought process:
In the 'modalController' I put an ng-model on the checkbox input to dynamically change the value of a variable (isChecked). When the input is checked it sets the $scope.isChecked value to true, when it's unchecked, $scope.isChecked is false.
In order to disable the button I would pass the value of 'isChecked' from the modalController to the custom directive where its value can be put in the ng-checked expression on the button located inside the directive template (see-below).
The Problem
When I try this solution, the console log shows an error saying "inputCheck is not defined". This happens as soon as the page loads, so the console log gets printed before the user can even click the checkbox. Any ideas on how to make this work?
Modal html:
<div ng-controller= "modalController">
<input type="checkbox" ng-model="isChecked">
<button-directive inputCheck="isChecked"></button-directive>
</div>
ButtonDirective.js:
angular.module('starter').directive('buttonDirective', function ($uibModal) {
return {
restrict: "EA",
templateUrl: "app/directives/button-directive.html",
scope: {
inputCheck: "#"
},
controller: ['$scope', function ($scope) {
console.log(inputCheck);
}]
};
});
button-directive.html:
<button ng-checked="inputCheck">
You are doing it wrong. $scope variable is different from that directive scope declaration.
Every child, including directives, are within $scope scope, get it?
So your directive declaration don't need that scope, remove it.
angular.module('starter').directive('buttonDirective', function ($uibModal) {
return {
restrict: "EA",
templateUrl: "app/directives/button-directive.html"
};
});
And your modal, no need to pass your inputCheck attribute.
<div ng-controller= "modalController">
<input type="checkbox" ng-model="isChecked">
<button-directive></button-directive>
</div>
Then your directive html becomes
<button ng-checked="isChecked">
See this
https://plnkr.co/edit/icaufi3LJxTnbTOkmxdb
Working the way you did.
You were passing attribute wrong, angularjs have a bad thing IMO that is when you are passing values to a directive like that inputCheck, you must write it different with hiphen so it needs to be input-check
<button-directive input-check="isChecked"></button-directive>
And your scope declaration must use equal sign
scope: {
inputCheck: "="
},
https://plnkr.co/edit/5jlHaE0fvhoDPi2h8XGq?p=preview
You should do following changes:
<button-directive input-check="isChecked"></button-directive>
scope: {
inputCheck: "="
}
<button ng-disabled="inputCheck">
I am working with the bootstrap-multiselect control. I need to execute multiselect() on a select element once all of the options have been populated by Angular.
Here is the select box, bound to a $scope property, with the options generated from an array of accountInfo objects snippet from my view:
<select id="property" multiple="multiple"
ng-model="selectedProperty"
ng-options="account.property_name group by account.client_name for account in accountInfo">
</select>
So once the options exist, I need to call $("#property").multiselect() to generate the bootstrap control, but I am looking for an event to listen for that tells me when all the options have been generated.
Instead of initializing bootstrap-multiselect control in a controller, do it in a directive link() function which is called after the corresponding element is rendered.
app.directive('multiselect', function() {
return {
restrict: 'A',
link: function(scope, element) {
element.multiselect();
}
};
});
Here's a demo.
I am a very confused as to why my ng-if expression is not evaluating like I think it should be. I am taking the ID of the button from the previous page, and trying to match that ID with a new ID on the next page with an ng-if expression.
My index.html page produces the amount of buttons based off my JSON object data, then when one gets clicked, it should go in my getButtonClicked scope with the correct ID of the button clicked. In the scope, I store this correct ID in a sessionStorage.
index.html
<div ng-controller="AppCtrl">
<button ng-repeat="x in data" id="{{x.id}}" ng-click="getButtonClicked(x.id)" onclick="window.location='secondPage.html';" >{{x.id}} {{x.userFirstName}} {{x.userLastName}}</button>
</div>
retrieveData.js
// creates a module
var myApp = angular.module("myModule",[]);
myApp.controller('AppCtrl' , function($scope, $http){
$http.get("thisurlworksjustreplacingip")
.success(function(response) {$scope.data = response;});
$scope.getButtonClicked = function(buttonNumber){
sessionStorage.setItem("school", buttonNumber);
}
});
function returnSchool(){
return (sessionStorage.getItem("school"));
}
Then on my secondPage.html I try to match up the sessionStorage variable, which I return in returnSchool() to the ID of a button in ng-if. So if you clicked the second button on the first page, it should show a button on the second page with that same data. Eventually I will change the data that is displayed on the button, I just want get the ID's matched properly before I do so.
secondPage.html
<div ng-controller="AppCtrl">
<button ng-repeat="x in data" id="{{x.id}}" ng-if="x.id == returnSchool()">X ID: {{x.id}} Get Value: {{returnSchool()}} {{x.userFirstName}} {{x.userLastName}}</button>
</div>
<script>console.log(returnSchool());</script>
The thing I do not understand is, when I try to print it out on the button using angular, returnSchool() will not print anything. When I print out returnSchool() in the console below, it prints out the correct ID of the button I clicked on the last page (1,2,3,4). If I change ng-if="x.id == returnSchool()", which displays no buttons, to ng-if="x.id != returnSchool()", it prints all the buttons, leaving me to believe that maybe returnSchool() does not equal the button id pressed on the previous page? I think something is just not evaluating properly in my ng-if.
On my page I have a table with a custom directive that shows row item's detailed view when a user click on details button :
<table ng-controller="MyController">
<tr ng-repeat="item in controller.items">
<td>{{item.name}}</td>
<td> details
</td>
</tr>
<tr ng-details details-colspan="2" ng-model="controller.details"></tr>
</table>
Directive:
.directive('ngDetails', function() {
return {
restrict: 'A',
require: "^ngModel",
replace: true,
template: '<tr class="detailsTemplate {{ngModel.cssClass}}"> \
<td colspan="{{detailsColspan}}" ng-bind-html="ngModel.data"></td> \
</tr>',
scope: {
detailsColspan: '#',
ngModel: '='
}
}
});
Here's a demo.
Right now item details section is showing fine, but it's always at the bottom of the table. I would like to move my directive row element under the corresponding item row, but I'm not sure how to access the directive element inside of my controller.
Rather than having the details row appear only once and be moved around, let the ng-repeat include it for every row. You can do this using ng-repeat-start on the first <tr> and ng-repeat-end on the last <tr>. This special syntax allows arbitrary sets of elements to be repeated without being contained in the same repeating element. Then you can include a ng-hide, ng-show, or possibly ng-if on the directive element to only be displayed when the appropriate row has been clicked.
More information about the ng-repeat-start/end syntax can be found here.
You shouldn't access your element inside your controller. If you have to do that, than there's something wrong with either your design of the application or your understanding of Angular itself.
At a general level, you should start thinking of your directives as the glue between HTML and business logic (your actual JS code inside your services, libraries, models, etc...) and be very strict of keeping it this way.
In this particular case, you could think of the whole ng-repeat as part of one directive, and incorporate the detail row between each item rows.
Even better, if showing the detail row requires some action from the user, like a click on the item row, you can dynamically append/remove the detail row just under the clicked item row. This will be a little bit more optimal since the detail data will be "lazy" compiled.
I'm using Angular 1.2 and bootstrap 3.x and I'm trying to collapse DIV using angular-bootstrap plugin. I have form with search bar that retrieves specific data from a server in the controller using my custom $resource service:
$scope.sendSearch = function(query) {
$scope.results = Search.query(query);
};
And I would like to un-collapse my results after retrieving them using this piece of code:
<div collapse="isCollapsed">
<li class="well" ng-repeat="result in results" style="list-style-type: none;">
</li>
</div>
I am able to to so after the data is retrieved by adding simple button with correct ng-click action, but my question is - is it possible to change the value of "isCollapsed" variable in the controller? I am aware that because of the "promise" nature of the response it could be problematic. When I tried to do so, my div freezed with class="collapsing" and nothing happened, hovewer - I was still able to collapse/uncollapse div using aforementioned button, which fixed class attribute too.
Just create a scope watch to see if results have changed or been retrieved.
$scope.watch("results", function(newValue, oldValue){
if (newValue.length > 0)
$scope.isCollapsed = !$scope.isCollapsed;
})
or
$scope.watch("results", function(newValue, oldValue){
if (_.isEqual(newValue, oldValue) ) // I used underscorejs
$scope.isCollapsed = !$scope.isCollapsed;
})