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
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>
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()"
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
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>
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.