How the controller in angular is called? - javascript

I want to bind the full name = firstname + lastname.
I have seen this example in the w3 schools. But i am unable to understand it.
My question is how the function got called? It have any listeners?
Could someone please, shed some light on it, in detailed way. Sorry I am newbie..
My code is:
var application = angular.module('myTempApp', []);
application.controller('myController', function ($scope) {
$scope.myFirstName = "xxx";
$scope.myLastName = "yyy";
$scope.myFunction = function () {
alert('called');
$scope.myFullName = $scope.myFirstName + $scope.myLastName;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body>
<div ng-app="myTempApp" ng-controller="myController">
<input type="text" ng-model="myFirstName" />
<input type="text" ng-model="myLastName" />
<input type="text" ng-model="myFullName" ng-bind="myFullName" />
<br />
{{ myFunction() }}
<br/>
data ::: {{2+2}}
</div>
</body>
Thanks in advance.

lets go step by step.
You are binding three literals and one function type variable. Whenever you write any variable (of any data type), it gets registered in the digest cycle of angularJS. So, firstName, lastName, fullName and myFunction gets registered in digest cycle. And every variable in digest cycle has a watcher.
Whenever a variable changes, angularJS checks through all variables registered in digest cycle and prints the latest value of each variable in the view.
so lets assume - if firstName is xxx and last name is yyy and you changed firstName to xx. Now angular will check both firstName and lastName and print the latest value of both of them.
Therefore, whenever you make any change to any scope variable, your binded function inside the angular expression gets called

If you want to update a variable depending on other could use $watch
var application = angular.module('myTempApp', []);
application.controller('myController', function ($scope) {
$scope.myFirstName = "xxx";
$scope.myLastName = "yyy";
$scope.myFunction = function () {
//alert('called');
$scope.myFullName = $scope.myFirstName + $scope.myLastName;
};
$scope.$watch("myFirstName + myLastName", $scope.myFunction);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body>
<div ng-app="myTempApp" ng-controller="myController">
<input type="text" ng-model="myFirstName" />
<input type="text" ng-model="myLastName" />
<input type="text" ng-model="myFullName" ng-bind="myFullName" />
<br />
{{ myFunction() }}
<br/>
data ::: {{2+2}}
</div>
</body>

This might help.
function studentController($scope) {
$scope.student = {
firstName: "Mahesh",
lastName: "Parashar",
fullName: function() {
var studentObject;
studentObject = $scope.student;
return studentObject.firstName + " " + studentObject.lastName;
}
};
}
Enter first name: <input type="text" ng-model="student.firstName"><br>
Enter last name: <input type="text" ng-model="student.lastName"><br>
<br>
You are entering: {{student.fullName()}}

There two ways of binding data between the model (html and the controller the data into the $scope, ng-bind and {{ }}. What they do here is call {{ myFunction() }} ((which act like a listener), then every time is taking care of myFirstName and myLastName (so as they change, the new value is going to be save in $scope.myfullName, and render into the model)

Related

Using 'Ng-If' "breaks" program on a $('#x').change or an angularfire $add

I've noticed two instances in my code where using ng-if causes my program to start working. On one, I do an ng-if="isOwnProfile", for an image-upload toolbar.
Using the ng-if causes the event listener to stop working. Code example:
$scope.myOwnProfile = false;
if (userLoggedIn === userUID) {
console.log('my images');
$scope.myOwnProfile = true;
} else {
console.log('not my images');
}
$("#image-upload").change(function(e) {
$scope.myOwnProfile = true;
var file = e.target.files[0];
var imageRef = firebase.storage().ref...
and in HTML:
<section ng-if="myOwnProfile">
<input id="image-upload" type="file" accept="image/*"> <br /><br />
</section>
In this case the event listener will stop working and not respond.
Another case is where you add a message to the page (and firebase).
Code:
$scope.addMessage = function(){
var date = new Date();
$scope.messages.$add({
timestamp: (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear(),
from: $scope.visitorRealName.name,
content: $scope.message
});
$scope.message = '';
};
HTML:
<section ng-if="AreWeFriends === true || myOwnProfile === true">
<form ng-submit="addMessage()">
<input ng-model="message">
<button type="submit">Add Message</button>
</form>
</section>
In the second case I get an error from Firebase "Key content was undefined. Cannot pass undefined in JSON. Use null instead".
I can't determine why using ng-if causes this to happen? What I do is set the user's profile to true whether they are a) a friend or b) it's the person's own profile (which is why I change $scope).
That's because the ngIf directive creates it's own child scope.
The ngIf directive creates it's own scope, so the ngModel directive containing message is set on the scope created by the ngIf directive, not your controller.
When you access the value in your controller it's not there, so it's undefined. You are essentially passing undefined to your content key inside your object you're adding to your messages so Firebase complains.
To fix it i'd recommend using the controller as syntax so that you can reference the controller, or use the ngShow directive, which doesn't create it's own child scope.
Here are a few examples of what happens:
(function() {
'use strict';
angular.module('app', []);
})();
(function() {
'use strict';
angular.module('app').controller('MainController', MainController);
MainController.$inject = ['$scope'];
function MainController($scope) {
var vm = this;
$scope.test1 = test1;
$scope.test2 = test2;
$scope.test3 = test3;
$scope.test4 = test4;
function test1() {
// this is undefined because there is no property `message`
// on the $scope of this controller
alert($scope.message);
}
function test2() {
// this contains the value binded to the message property
// of this controller because we used the controller as syntax
alert(vm.message);
}
function test3(message) {
// because we are passing in the message value we can
// access it without caring where it came from
alert(message);
}
function test4() {
// the property `message` exists on this $scope because we
// used the ngShow directive instead of the ngIf
alert($scope.message4);
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController as MainCtrl">
<h4>Without controller as syntax</h4>
<p>Does not work because message is not set on the controller's $scope, it's set on the scope created by the ngIf directive</p>
<form ng-if="true">
<input ng-model="message">
<button ng-click="test1()">Submit</button>
</form>
<hr>
<h4>With controller as syntax</h4>
<p>Works because message is set on the controller so we can access it using `this` in our MainController</p>
<form ng-if="true">
<input ng-model="MainCtrl.message">
<button ng-click="test2()">Submit</button>
</form>
<hr>
<h4>Without controller as syntax but passing the message value into our function</h4>
<p>Works because although message is set on the scope created by the ngIf directive, we are passing it to test3 in our MainController.</p>
<form ng-if="true">
<input ng-model="message">
<button ng-click="test3(message)">Submit</button>
</form>
<hr>
<h4>With ngShow directive instead of ngIf</h4>
<p>Works because message is set on $scope from our contoller</p>
<form ng-show="true">
<input ng-model="message4">
<button ng-click="test4()">Submit</button>
</form>
</div>

Pass value from input to controller without $scope

I have problem with following code. I can solve the issue with $scope, but this time request to do it without using $scope in controller. I am using "controller as" to control the view.
<body ng-app="appModule" >
<div ng-controller="calculatorController as calc">
<input type="number" name="firstDigit" placeholder="insert num" ng-model="calc.firstDigit">
<input type="number" name="secondDigit" placeholder="insert num" ng-model="calc.secondDigit">
<span>{{calc.result}}</span>
</div>
</body>
(function(){
angular
.module("calculatorModule")
.controller("calculatorController", calculatorController)
function calculatorController(){
var calc = this;
calc.result = calc.firstDigit + calc.secondDigit;
}
})();
Well, you have two options - you can do it with watchers, or with a function to get the result. I prefer the latter, but it's up to you. Here's an example of how you can get it to work:
Side note - learn the controller as syntax, it will save you thousands of headaches down the road with nested scoping and child-parent relationship issues with $scope -- here's a great article explaining controller as
(function () {
angular.module("calculatorModule", [])
.controller("calculatorController", [function() {
var calc = this;
calc.getResult = function() {
return calc.firstDigit + calc.secondDigit;
}
calc.result = calc.getResult();
}]);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="calculatorModule" ng-controller="calculatorController as calc">
<input type="number" name="firstDigit" placeholder="insert num" ng-model="calc.firstDigit">
<input type="number" name="secondDigit" placeholder="insert num" ng-model="calc.secondDigit">
<span>{{calc.getResult()}}</span>
</div>
Your module definition is false!
Should be
module("calculatorModule", [])

Updating an input value is updating all the corresponding input values in the array in angularJS

Here is my JS code.
angular.module('myApp', [])
.controller("myController", [function () {
this.master = [];
this.addUser = function(user) {
this.master.push(user)
};
this.removeUser = function(user) {
var indexToRemove = this.master.indexOf(user);
this.master.splice(indexToRemove,1)
}
this.reset = function() {
this.user = this.master[this.master.length - 1];
}
}])
Here is my HTML part.
<body ng-app="myApp" ng-controller="myController as Cntrl">
<form>
Name: <input type="text" ng-model="user.name" /> <br />
Email: <input type="email" ng-model="user.email" /> <br />
Gender: <input type="radio" ng-model="user.gender" value="male" /> Male
<input type="radio" ng-model="user.gender" value="female" /> female <br /> <br />
<input type="button" ng-click="Cntrl.addUser(user)" value="Add User">
<input type="button" ng-click="Cntrl.reset()" value="Reset User">
</form>
<div ng-repeat="users in Cntrl.master track by $index">
<span ng-click="Cntrl.removeUser(users)" >X</span> <pre>{{users | json}}</pre>
</div>
</body>
I am able to add new users and remove the selected users. But whenever I add any new users, the properties of all the old users in the array are getting updated with the properties of the newly added user.
Please explain the mistake I am making here.
all users pushed into array are the same one, and remove better use index,
ng-click="Cntrl.removeUser($index)"
angular.module('myApp', [])
.controller("myController", [
function() {
this.master = [];
this.user = this;
//use this.user
this.addUser = function() {
this.master.push(this.user)
this.user = {}
};
//use index
this.removeUser = function(index) {
this.master.splice(indexToRemove, 1)
}
this.reset = function() {
this.user = this.master[this.master.length - 1];
}
}
])
you are giving user reference to master array , and because of two way data binding your values are updated , can u try this, will work.
just modify your addUser method as below.
this.addUser = function(user) {
this.master.push(angular.copy(user));
};
angular copy will create deep copy of object , see docs https://docs.angularjs.org/api/ng/function/angular.copy
As there has already been 3 posts that answers your question. But I
guess there are some serious logical misunderstanding on the VM
approach your are trying to implement in your code. Though the program
is now working but I highly recommend you to go through the John
Papa's tutorial before proceeding with your solution. This would clear
your fundamentals and make you understand why it wasn't working
before.
https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/

How to add multiple items to a list

I'm building an app where users can add items to a list and I decided, for the sake of learning, to use Angular (which I'm very new to). So far, I've been able to successfully add a single item to that list without any issues. Unfortunately, whenever I try to add more than one without a page refresh, I get an error - specifically a "Undefined is not a function."
I've spent more time than I care to think about trying to resolve this issue and I'm hoping an expert out there can give me a hand. Here's what I have so far:
Controllers:
angular.module('streakApp')
.controller('StreakController', function($scope) {
// Removed REST code since it isn't relevant
$scope.streaks = Streak.query();
// Get user info and use it for making new streaks
var userInfo = User.query(function() {
var user = userInfo[0];
var userName = user.username;
$scope.newStreak = new Streak({
'user': userName
});
});
})
.controller('FormController', function($scope) {
// Works for single items, not for multiple items
$scope.addStreak = function(activity) {
$scope.streaks.push(activity);
$scope.newStreak = {};
};
});
View:
<div class="streaks" ng-controller="FormController as formCtrl">
<form name="streakForm" novalidate >
<fieldset>
<legend>Add an activity</legend>
<input ng-model="newStreak.activity" placeholder="Activity" required />
<input ng-model="newStreak.start" placeholder="Start" type="date" required />
<input ng-model="newStreak.current_streak" placeholder="Current streak" type="number" min="0" required />
<input ng-model="newStreak.notes" placeholder="Notes" />
<button type="submit" ng-click="addStreak(newStreak)">Add</button>
</fieldset>
</form>
<h4>Current streaks: {{ streaks.length }}</h4>
<div ng-show="newStreak.activity">
<hr>
<h3>{{ newStreak.activity }}</h3>
<h4>Current streak: {{ newStreak.current_streak }}</h4>
<p>Start: {{ newStreak.start | date }}</p>
<p>Notes: {{ newStreak.notes }}</p>
<hr>
</div>
<div ng-repeat="user_streak in streaks">
<!-- Removed most of this for simplicity -->
<h3>{{ user_streak.fields }}</h3>
</div>
</div>
Could you post the html of StreakController too? Your solution works fine in this fiddle:
http://jsfiddle.net/zf9y0yyg/1/
.controller('FormController', function($scope) {
$scope.streaks = [];
// Works for single items, not for multiple items
$scope.addStreak = function(activity) {
$scope.streaks.push(activity);
$scope.newStreak = {};
};
});
The $scope inject in each controller is different, so you have to define the "streaks" in FormController.
Your problems comes from :
.controller('FormController', function($scope) {
// Works for single items, not for multiple items
$scope.addStreak = function(activity) {
$scope.streaks.push(activity);
^^^^^^
// Streaks is initialized in another controller (StreakController)
// therefore, depending of when is instantiated StreakController,
// you can have an error or not
$scope.newStreak = {};
};
});
A better design would be to implement a StreakService, and to inject that service in the controller you need it. Of course, initializing $scope.streaks in FormController will make your code work, but that's not the responsibility of FormController to initialize this data.
I assume FormController is a nested controller of StreakController, so they share the same scope.
if that works for single object, it should work for mulitiple objects, the problems is you can't just use push to push an array of object to the streaks, you can for loop the array and add them individually or use push.apply trick. I thought the reason of Undefined is not a function. is because the Stack.query() return an element instead of an array of elements so, the method push doesn't exists on the $scope.streaks.
http://jsbin.com/jezomutizo/2/edit

Read content from DOM element with KnockoutJS

The goal
Read content from DOM element with KnockoutJS.
The problem
I have a list of products in my HTML. The code is something like this:
<li>
<div class="introduction">
<h3>#Model["ProductName"]</h3>
</div>
<form data-bind="submit: addToSummary">
<input type="number"
placeholder="How much #Model["ProductName"] do you want?" />
<button>Add product</button>
</form>
</li>
When I click on <button>Add Product</button>, I want to send to KnockoutJS the text inside <h3></h3> of the element that was submitted.
The file to work with KnockoutJS is external and independent of HTML. It name is summary.js and the code is below:
function ProductReservation(id, name, unity, quantity) {
var self = this;
self.id = id;
self.name = name;
self.unity = unity;
self.quantity = quantity;
}
function SummaryViewModel() {
var self = this;
self.products = ko.observableArray([
new ProductReservation(1, "Testing", "kgs", 1)
]);
self.addToSummary = function () {
// Do Something
}
}
What I'm thinking about
HTML:
<li>
<div class="introduction">
<h3 data-bind="text: productName">#Model["ProductName"]</h3>
</div>
[...]
</li>
JS:
productName = ko.observable("text: productName");
But, of course, no success — this is not the correct context or syntax, was just to illustrate.
So I ask: what I need to do?
You're binding addToSummary via the submit binding. By default KO sends the form element to submit-bound functions.
So addToSummary should have a parameter -
self.addToSummary = function (formElement) {
// Do Something
}
You can pass additional parameters to this function (as described in KO's click binding documentation under 'Note 2'), or you could just add a hidden field to your form element and pull it from there.
<li>
<div class="introduction">
<h3>#Model["ProductName"]</h3>
</div>
<form data-bind="submit: addToSummary">
<input type="number" name="quantity"
placeholder="How much #Model["ProductName"] do you want?" />
<input type="hidden" name="productName" value="#Model["ProductName"]" />
<button>Add product</button>
</form>
</li>
Then in your knockout code you could use jQuery to process the form element to pull out the data -
self.addToSummary = function (formElement) {
var productName = $(formElement).children('[name="productName"]')[0].val();
var quantity= $(formElement).children('[name="quantity"]')[0].val();
// ...
self.products.push(new ProductReservation(?, productName, ?, quantity));
}
One strategy that has worked well in my experience is to implement a toJSON extension method that serializes your model (if you use a library like JSON.NET you can have a lot of control over what gets serialized and what does not).
Inside of the view where you initialize KnockoutJS, you could serialize your model and pass it into the KnockoutJS ViewModel you're creating (assuming :
Main view:
<script type="text/javascript">
var viewModel = new MyViewModel(#Model.ToJSON());
</script>
ViewModel:
function MyViewModel(options) {
this.productName = ko.observable(options.ProductName);
}

Categories

Resources