Angularjs two similar scopes one updates but the other doesn't - javascript

I've tried to create a very simple example of the issue I'm having with AngularJS. I've got a simple scope called testScope. I also have 2 other scopes (grouped1 and grouped2) that are derived from testScope that have been altered using a grouping function found in UnderscoreJs.
script.js
var app = angular.module('testScope', []);
app.controller('mainCtrl', function($scope) {
$scope.testScope = {
test1: {
data: [
{
field1: 'blah',
field2: 'blah blah'
},
{
field1: 'test',
field2: 'test test'
}
]
}
};
$scope.createEntry = function(newEntry) {
$scope.test1.data.push({field1: newEntry.field1, field2: newEntry.field2});
};
$scope.test1 = $scope.testScope['test1'];
$scope.grouped1 = _.groupBy($scope.test1, 'field1');
$scope.grouped2 = _.groupBy($scope.test1.data, 'field1');
});
index.html
<body ng-app="testScope" ng-controller="mainCtrl">
<form ng-submit="createEntry(newEntry)">
Field1: <input type="text" ng-model="newEntry.field1" />
Field2: <input type="text" ng-model="newEntry.field2" />
<input type="submit" />
</form>
data
<div> {{ test1 }} </div><br>
grouped1
<div>{{ grouped1 }}</div><br>
grouped2
<div>{{ grouped2 }}</div>
</body>
The problem is that when I modify my scope (using the form), test1 and grouped1 will update but grouped2 will not. Why doesn't grouped2 update and how do I get grouped2 to update when the scope changes?
Please see my example:
http://plnkr.co/edit/IN8lADekDBxDp1CNf8VG?p=preview

the reference that .groupBy($scope.test1.data, 'field1') creates changes each time $scope.test1.data changes1. Since $scope works based off of the reference, changing that allows the data to become stale or outdated.
To fix this, you can simply wrap the scope in a function. Such as this:
$scope.grouped2 = function() {return _.groupBy($scope.test1.data, 'field1');};
And then just change your reference in your html like so:
grouped2
<div>{{ grouped2() }}</div>
plunkr: here

Related

Creating models dynamically

This is a little bit complicated to explain. I need to create an object with properties from a dynamic html code. Let me explain it with an example.
I have an object with my data like this
var myObject = {Field1: 'Value1', Field2: 'Value2'};
I have an array with my object properties like this:
var myArray = ['Field1', 'Field2'];
I use the array to generate <input> in a for loop like this:
<div v-for="property in myArray">
<input type="text" :value="myObject[property]" />
</div>
Now, I need to get the values from the generated inputs (can be an object or an array).
I know i can just simply take myObject but the thing is that the values might change (The user can alter the data because is an input which is the right approach). Also I dont want to bind the inputs to myObject because even in the input values change, myObject need to remain as it initial state.
The question is how can I create a new object and bind my inputs to that new object?
The solutions:
v-model one computed like below, it has one disadvantage, Vue won't catch the modification to re-render.
create one clone myOjbect then watch it, if change, do something you like.
similar with solution 2: using v-bind, then bind input event to assign the value to another object or something else .
PS: v-model do same thing like this.
app = new Vue({ //not vue, it is Vue
el: "#app",
data() {
return {
myObject: {Field1: 'Value1', Field2: 'Value2'},
myArray: ['Field1', 'Field2'],
copyMyObject: {}, //solution 2
copyMyObject1: {} //solution 3
}
},
computed: {//solution 1
computedObject: function(){
return Object.assign({}, this.myObject)
}
},
mounted: function(){ //solution 2
this.copyMyObject= Object.assign({}, this.myObject)
this.copyMyObject1= Object.assign({}, this.myObject)
},
watch: {//solution 2
copyMyObject: function (newValue, oldValue){
console.log('copyMyObject', newValue)
//do something else you'd like
}
},
methods: {
getObject: function () {//solution 1
console.log('computedObject', this.computedObject)
console.log('myObject', this.myObject)
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<button #click="getObject()">Click me!</button>
<p>The disadvantage: {{computedObject}}</p>
<div v-for="property in myArray">
<input type="text" v-model="computedObject[property]" />
</div>
<p>Org: {{myObject}}</p>
<p>Copy: {{copyMyObject}}</p>
<div v-for="property in myArray">
<input type="text" v-model="copyMyObject[property]" />
</div>
<p>Copy: {{copyMyObject1}}</p>
<div v-for="property in myArray">
<input type="text" v-bind:value="copyMyObject1[property]" #input="copyMyObject1[property] = $event.target.value" />
</div>
</div>
You create the object from the original object:
data: {
objectValues = Object.assign({}, myObject)
}
then you can use that object in your template:
<div v-for="property in myArray">
<input type="text" v-model="objectValues[property]" />
</div>

How do I properly submit an object into an array using Angular Js

I'm trying to learn AngularJs and writing some throw away code. I'm trying to create an object Bookmark and push it into an array.
HTML:
<h2>Create a new bookmark </h2>
<form class="form-group" ng-submit="createBookmark(newBookmark)" novalidate>
<!--Title-->
<h4>Bookmark Title</h4>
<input type="text" ng-model="newBookmark.title">
<!--Url-->
<h4>Bookmark Url</h4>
<input type="text" ng-model="newBookmark.url">
<!--Submit-->
<button type="submit" href="#" class="btn btn-primary" id="crForm" ng-click="stopCreating()">Save</button>
</form>
JS:
function resetCreateForm(){
$scope.newBookmark = {
title : '',
url : '',
category : $scope.currentCategory.name
};
}
function createBookmark(bookmark) {
bookmark.id = $scope.bookmarks.length;
bookmark.category = $scope.currentCategory.name;
$scope.bookmarks.push(bookmark);
resetCreateForm();
}
$scope.createBookmark = createBookmark;
$scope.resetCreateForm = resetCreateForm;
Object:
$scope.bookmarks = [
{id: 0, title: "Title1", url: "www.Title1.com", category: "Development"},
{id: 1, title: "Title2", url: "www.Title2.com", category: "Development"}
];
Module and Controller:
var app = angular.module('list',[]);
app.controller('listController', function($scope){
For some reason it does not work, so far I think it's from changes in the Angular version but I could not find a way to make it work.
You have to bind resetCreateForm & createBookmark in $scope of controller so that you can access them from view.
//place this inside your controller
$scope.resetCreateForm = resetCreateForm;
$scope.createBookmark= createBookmark;
Also you don't need to call function on ng-click, on click of button ng-submit will get called. Remove ng-click="stopCreating()"

accessing collection inside json object angular

I am new to angular
in the following controller i need to access the object store in my html. But it is not working. Any help
(function () {
'use strict';
angular.module('app').controller('BookController', ['$scope', function ($scope) {
$scope.book = {
id: 1,
name: 'Harry Potter',
author: 'J. K. Rowling',
stores: [
{ id: 1, name: 'Barnes & Noble', quantity: 3 },
{ id: 2, name: 'Waterstones', quantity: 2 },
{ id: 3, name: 'Book Depository', quantity: 5 }
]
};
}]);
});
<div ng-controller="BookController">
{{book.stores}}
</div>
You need to first invoke your anonymous function first using () after the final closing bracket and before the final semi-colon so that the last line looks like this: })();.
You should define angular module first and then amend it with the angular component like controller, service , factory, directive, filters, etc.
angular.module('app', [])
then add ng-app="app" on your page.
Markup
<div ng-app="app" ng-controller="BookController">
{{book.stores}}
</div>
Plunkr Here
Update
If suppose you have multiple store inside the stores object, and you want to show them on the html, then for that you could ng-repeat directive. It will repeat each element on html
<div ng-repeat="s in book.stores">
<span>{{s.name}}</span>
<input type="text" ng-model="s.name" />
<input type="numeric" ng-model="s.quantity" />
</div>
Updated Plunkr

Angular - Get ng-repeat object

I need to get the current object out of an ng-repeat on ng-click, I can't use $index because I'm using orderBy and therefore it gives me the wrong index relative to the scope object. Idealy I want to be able to click on the object (thumbnail) and have $scope.activePerson gain all that objects values.
My data is structured as follows:
[
{
'name': 'john'
},
{
'name': 'toby'
},
{
'name': 'sarah'
}
]
This is very much simplified, my real objects have 30+ KV pairs and subobjects. There are 10 objects that I'm repeating from (in batches of 4).
My current HTML is:
.item.fourth(ng-repeat="person in people | orderBy:'-name' " ng-show="$index <= 3" nid="{{person.guid}}"
Thanks
It's just person in ng-repeat="person in people";
I'm not sure what kind of markdown you're using, you definitely don't have html there, but you want something like:
<div
ng-repeat="person in people | orderBy:'-name' "
ng-show="$index <= 3"
nid="{{person.guid}}"
ng-click="activePerson = person">
</div>
Note that ng-repeat creates a child scope, so you'll want to have activePerson already set in the parent scope.
You can just use orderBy and copy the current object from ng-repeat, see this plunkr. Relevant code:
Controller
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.stuff = [
{
'name': 'john'
},
{
'name': 'toby'
},
{
'name': 'sarah'
}
];
$scope.setActivePerson = function (obj) {
$scope.activePerson = obj;
};
});
View
<body ng-controller="MainCtrl">
<div ng-repeat="thing in stuff | orderBy: 'name'">
<input type="radio" ng-click="setActivePerson(thing)" />
{{ thing.name }}
</div>
<br />
<div ng-model="activePerson">Active: {{ activePerson.name }}</div>
</body>

AngularJS server form validation / Javascript: Parsing javascript variable "path"

I'm building an Angular app connected to an REST service which does server-side input validation.
e.g. if I send a object to the server with JSON like:
entry: { id: 5, name: "Test", locales: ["de","en"] }
I will get an response like:
{ id: 5, name: "Test", countries: ["de","en"],
__errors__: [
{ field: "entry.name", message: "Test already in use" },
{ field: "entry.countries[1]", message: "'en' is not a country" }
]
}
(quotation marks omitted for better reading)
The field value is the "path" in javascriptish notation to the original value which caused the problem.
I'm somewhat free in what notation I will choose but I like this one because it's easy to read and integrates with the rest of the system. But I'm open to better suggestions.
The Question:
Now I want Angular to show which field failed with which message. What's the best way of doing this?
I tried things like $scope.EditForm.$setValidity( field, message ) but it has no effect.
(nb: I'm using Angular with Bootstrap)
Why not return an errors object that you bind to in your markup?
Here's a fiddle: http://jsfiddle.net/edeustace/UMRU9/2/
Here's a snippet:
<div ng-app="App" ng-controller="Ctrl">
<input id="name" type="text" ng-model="name"></input>
<span ng-show="errors.name" style="color: red">{{errors.name}}</span>
<br/>
<input id="lastName" type="text" ng-model="lastName"></input>
<span ng-show="errors.lastName" style="color: red">{{errors.lastName}}</span>
<div ng-repeat="c in countries">
<input ng-model="c" type="text"></input>
<span style="color: red" ng-show="errors.countries[$index]">{{errors.countries[$index]}} </span>
</div>
<button ng-click="submit()">Submit</button>
</div>
Js:
var app = angular.module('App', []);
app.controller('Ctrl', function($scope){
$scope.name = "Ed";
$scope.lastName = "Eustace";
$scope.countries = ["Ireland", "England"];
$scope.submit = function(){
$scope.errors = {name: "Name in use", lastName: "", countries: ["Ireland is not available"] };
}
});
That way you only need to wire up the ui and the update will happen for free due to data binding.

Categories

Resources