Reactivity to an array element based on object property - arrays

I am getting student details in summary form, just name and surname. I am passing the response to an array in data: data: function(){ return{ studsSummary:[] }}. I am using this stud array in a v-for to create the students dynamically like this: <div>{{studSummary.name}}</div>.
On selecting a student, I am loading the student details in detailed form beneath the summary area. The detailed details have an object inside data with corresponding fields. Let's say just name and surname only for my question. So in data I also have studDetailed : { name: '', surname: ''}. Further more these are now bound to a textfield through v-model='studDetailed.name'. Now since studsSummary[0].name = studDetailed.name I want the former to react to the latter and have any changes reflect in the summary also as I type in the textfield.
What do I have to change please to make this work? I have then tried this.$set(this.studsSummary[0], 'name', this.studDetailed.name) to bind the two (it's ultimately what I want), but that didn't work.

If I understand you correctly, try like following snippet:
(update studsSummary on #input event)
new Vue({
el: '#demo',
data(){
return {
studsSummary: [{id: 1, name: 'aaa', surname: 'AAA'}, {id:2, name: 'bbb', surname: 'BBB'}, {id:3, name: 'ccc', surname: 'CCC'}],
studDetailed: {id: null, name: '', surname: ''}
}
},
methods:{
selectStud(stud) {
this.studDetailed = stud
},
setStud() {
this.studsSummary = this.studsSummary.map((item) =>
item.id !== this.studDetailed.id
? item
: { ...item, ...this.studDetailed }
)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<ul>
<li v-for="(studSummary, i) in studsSummary" :key="i">
<div #click="selectStud(studSummary)">{{studSummary.name}} - {{studSummary.surname}}</div>
</li>
</ul>
<div v-if="studDetailed.id">
<input type="text" v-model="studDetailed.name" #input="setStud" />
<input type="text" v-model="studDetailed.surname" #input="setStud" />
</div>
</div>

Related

How to filter ng-repeat list on empty string values with an inputfield?

How can I filter an ng-repeat to show all items where a certain columnfield is an empty string? When I try this by typing nothing in the field it always seem to give the full list (wich is expected). I only want to see the person with id 1. How can I adjust the filter that a certain character in the inputfield makes the ng repeat filter on empty fields for the name column.
FiddleJs Example
My view:
<div class="panel-heading">
<h3>Filtered by {{filterValue}}</h3>
<input ng-change="filter()" ng-model="filterValue"/>
</div>
<div class="panel-body">
<ul class="list-unstyled">
<li ng-repeat="p in filteredPeople">
<h4>{{p.name}} ({{p.age}}) id: {{p.id}}</h4>
</li>
</ul>
</div>
Controller:
var people = [{
name: '',
age: 32,
id: 1
}, {
name: 'Jonny',
age: 34,
id: 2
}, {
name: 'Blake',
age: 28,
id: 3
}, {
name: 'David',
age: 35,
id: 4
}];
$scope.filter = function (value) {
$scope.filteredPeople = $filter('filter')(people, {
name: $scope.filterValue
});
}
$scope.people = people.slice(0);
Delete your $scope.filter() function in the controller and the ng-change="filter()" in your view. You should change var people array to $scope.people. You also need to delete the line $scope.people = people.slice(0);.
Create a filter function in your controller to only return people whose name property is empty if $scope.filterValue is empty:
$scope.emptyFilter = function(person) {
if ($scope.filterValue.length === 0) {
if (person.name.length === 0) {
return person;
}
} else {
return person;
}
};
Next, update your ng-repeat with the following:
<li ng-repeat="p in people | filter:emptyFilter | filter:{name: filterValue}">

How to update formControl values automatically from other formControl values in reactive forms angular 2?

this.myForm = fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
date: ['', [Validators.required, Validators.minLength(2)]],
address: ['', [Validators.required, Validators.minLength(2)]],
,
items: fb.array([
this.initItem(),
])
});
initItem() {
return this.fb.group({
item: [''],
itemType: [''],
amount: [''],
presentRate:this.myForm,
total:['']
});
When submitting the form,this item property will be stored by an object.
Example object:
item{
itemName:"name",
itemRate:1000,...}
How can i use the properties of item object and patch the values in my initItem() methord properties?My scenario is like ,When user select a value from dropdown,the item will get updated and i would like to display the values obtained from the item in other formControls.
Example:
<div *ngFor="let item of myForm.controls.items.controls; let i=index">
<div [formGroupName]="i">
<md2-autocomplete [items]="products"
item-text="product"
(change)="handleChange($event)"
placeholder="Product purchased"
formControlName="item"
>
</md2-autocomplete>
<md-input-container >
<input md-input placeholder="Present rate" [value]="presentRate" formControlName="presentRate" >
</md-input-container>
I would like to update the values on presentRate input box automatically.
You can subscribe to valueChanges of a form control and call setValue on another form control.
this.myForm.get('myControlName').valueChanges
.subscribe(val =>
this.myForm.get('myOtherControlName').setValue(val)
);
I'm supposing that you're trying to update the value for each presentRate based on the selected value in md2-autocomplete. If I'm correct the following should work:
Template:
(change)="handleChange($event, i)"
Component:
handleChange($event: any, i: index) {
const control: AbstractControl = myForm.get(`items.${i}.presentRate`);
let newVal: any;
if ($event.value) {
newVal = $event.value.rate;
} else {
newVal = '';
}
control.patchValue(newVal);
}

Angular-xeditable: Need a checklist that displays checked items

I would like to use a check list and show the user the boxes she has checked.
I am using this framework: http://vitalets.github.io/angular-xeditable/#checklist . See his example 'Checklist' versus his example 'Select multiple'. However, I do not want to display a link with a comma separated string, i.e., join(', '). I would like each selection to appear beneath the previous, in an ordered list or similar.
Pretty much copied from his examples, here are the guts of my controller:
$scope.userFeeds = {
feeds: {}
};
$scope.feedSource = [
{ id: 1, value: 'All MD' },
{ id: 2, value: 'All DE' },
{ id: 3, value: 'All DC' }
];
$scope.updateFeed = function (feedSource, option) {
$scope.userFeeds.feeds = [];
angular.forEach(option, function (v) {
var feedObj = $filter('filter')($scope.feedSource, { id: v });
$scope.userFeeds.feeds.push(feedObj[0]);
});
return $scope.userFeeds.feeds.length ? '' : 'Not set';
};
And here is my html:
<div ng-show="eventsForm.$visible"><h4>Select one or more feeds</h4>
<span editable-select="feedSource"
e-multiple
e-ng-options="feed.id as feed.value for feed in feedSource"
onbeforesave="updateFeed(feedSource, $data)">
</span>
</div>
<div ng-show="!eventsForm.$visible"><h4>Selected Source Feed(s)</h4>
<ul>
<li ng-repeat="feed in userFeeds.feeds">
{{ feed.value || 'empty' }}
</li>
<div ng-hide="userFeeds.feeds.length">No items found</div>
</ul>
</div>
My problem is - display works with editable-select and e-multiple, but not with editable-checklist. Swap it out and nothing is returned.
To workaround, I have tried dynamic html as in here With ng-bind-html-unsafe removed, how do I inject HTML? but I have considerable difficulties getting the page to react to a changed scope.
My goal is to allow a user to select from a checklist and then to display the checked items.
Try this fiddle: http://jsfiddle.net/mr0rotnv/15/
Your onbeforesave will need to return false, instead of empty string, to stop conflict with the model update from xEditable. (Example has onbeforesave and model binding working on the same variable)
return $scope.userFeeds.feeds.length ? false : 'Not set';
If you require to start in edit mode add the attribute shown="true" to the surrounding form element.
Code for completeness:
Controller:
$scope.userFeeds = {
feeds: []
};
$scope.feedSource = [
{ id: 1, value: 'All MD' },
{ id: 2, value: 'All DE' },
{ id: 3, value: 'All DC' }
];
$scope.updateFeed = function (feedSource, option) {
$scope.userFeeds.feeds = [];
angular.forEach(option, function (v) {
var feedObj = $filter('filter')($scope.feedSource, { id: v });
if (feedObj.length) { // stop nulls being added.
$scope.userFeeds.feeds.push(feedObj[0]);
}
});
return $scope.userFeeds.feeds.length ? false : 'Not set';
};
Html:
<div ng-show="editableForm.$visible">
<h4>Select one or more feeds</h4>
<span editable-checklist="feedSource"
e-ng-options="feed.id as feed.value for feed in feedSource"
onbeforesave="updateFeed(feedSource, $data)">
</span>
</div>
<div ng-show="!editableForm.$visible">
<h4>Selected Source Feed(s)</h4>
<ul>
<li ng-repeat="feed in userFeeds.feeds">{{ feed.value || 'empty' }}</li>
<div ng-hide="userFeeds.feeds.length">No items found</div>
</ul>
</div>
Css:
(Used to give the "edit view" a list appearance)
.editable-input label {display:block;}
Also there is the option of using a filter if you do not need to do any validation or start in edit mode.
Controller:
$scope.user = { status: [2, 3] };
$scope.statuses = [
{ value: 1, text: 'status1' },
{ value: 2, text: 'status2' },
{ value: 3, text: 'status3' }
];
$scope.filterStatus = function (obj) {
return $scope.user.status.indexOf(obj.value) > -1;
};
HTML:
<a href="#" editable-checklist="user.status" e-ng-options="s.value as s.text for s in statuses">
<ol>
<li ng-repeat="s in statuses | filter: filterStatus">{{ s.text }}</li>
</ol>
</a>

How to insert dynamically a text in an object "angular"?

I would like to be able to select a specific object and enter a text in it 'dynamically' in Angular.
App file:
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
$scope.persons = [
{
name: "Kevin",
lastName: "Cris",
mottos : [
{
motto: "Holy Moly!"
}
],
},
{
name: "Tres",
lastName: "Yepo",
mottos : [
{
motto: "Crispy!"
}
],
},
{
name: "Prosi",
lastName: "Nani",
mottos : [
{
motto: "I love this!"
}
],
}
];
}]);
I can insert a text in the object in array[1]:
$scope.addMotto = function(){
$scope.persons[1].mottos.push({
motto: $scope.enterMotto
});
};
But, how could I be able to add motto to a person object each time?
To be more clear: What I am trying to do is: Select a person and add motto.
What I can't figure out is: How to create selection of a person object and add on selected object a text.
Html file:
<div ng-controller="myCtrl">
<section ng-repeat="person in persons">
<p class="header">Persons --</p>
<p>Name: {{person.name}}</p>
<p>lastName: {{person.lastName}}</p>
<p ng-repeat="shoutOut in person.mottos" class="motto">Motto: {{shoutOut.motto}} </p>
</section>
<p ng-repet="newMotto in persons.mottos">Your Motto: {{newMotto.motto}} </p>
<p>Enter Motto:<input type="text" ng-model="enterMotto" /></p>
<button ng-click="addMotto()">Submit </button>
</div><!--myCtrl-->
Live: http://jsfiddle.net/9eau9dq2/
If you want to add to each of the person object, here is what you should do - http://jsfiddle.net/k8o8x60w/
$scope.addMotto = function(){
angular.forEach($scope.persons, function(item, i){
item.mottos.push({motto: $scope.enterMotto});
});
};
If you want to add to the selected person object, you will need to keep the selected id with you and update the id whenever a different person is selected and update his motto accordingly.
You could provide a select box like this to allow user to select a person to which you would want to add a motto
<select>
<option ng-repeat="person in persons" value="{{person.name}}">{{ person.name }} {{ person.lastName }}</option>
</select>
and update your method to...
$scope.addMotto = function () {
angular.forEach($scope.persons, function (item, i) {
if (item.name == $scope.selectedperson) {
item.mottos.push({
motto: $scope.enterMotto
});
}
});
};
Updated fiddle : http://jsfiddle.net/k8o8x60w/1/
Note: Would strongly advice you to use some id too in the person object.
i updated your fiddle with some working code!
http://jsfiddle.net/9eau9dq2/1/
You could also use a select box for sure, but that is really similar anyways ;-)
Hope that helps!
Jan

AngularJS checkbox filter directive

I have a AngularJS directive that allows users to select a values from a list to filter on. Pretty simple concept which is represented here:
Problem is when I click one of the checkboxes they all select unintended. My directive is pretty simple so I'm not sure why this is happening. The code around the selection and checkboxes is as follows:
$scope.tempFilter = {
id: ObjectId(),
fieldId: $scope.available[0].id,
filterType: 'contains'
};
$scope.toggleCheck = function (id) {
var values = $scope.tempFilter.value;
if (!values || !values.length) {
values = $scope.tempFilter.value = [];
}
var idx = values.indexOf(id);
if (idx === -1) {
values.push(id);
} else {
values.splice(idx, 1);
}
};
$scope.valuesListValues = function (id) {
return $scope.available.find(function (f) {
return f.id === id;
}).values;
};
and the data resembles:
$scope.available = [{
id: 23,
name: 'Store'
values: [
{ id: 124, name: "Kansas" },
{ id: 122, name: "Florida" }, ... ]
}, ... ]
the view logic is as follows:
<ul class="list-box">
<li ng-repeat="val in valuesListValues(tempFilter.fieldId)">
<div class="checkbox">
<label ng-click="toggleCheck(val.id)">
<input ng-checked="tempFilter.value.indexOf(val.id) === -1"
type="checkbox"> {{val.name}}
</label>
</div>
</li>
</ul>
First off, it toggleCheck fires twice but populates the correct data ( second time given my code it removes it though ).
After the second fire, it checks all boxes... Any ideas?
Perhaps its that the local variable doesn't get reassigned to the property of the scope property used in the view. Since your values are then non-existent and not found, the box is checked.
$scope.tempFilter.value = values
I took the interface concept you were after and created a simpler solution. It uses a checked property, found in each item of available[0].values, as the checkbox model. At the top of the list is a button that clears the selected items.
JavaScript:
function DataMock($scope) {
$scope.available = [{
id: 23,
name: 'Store',
values: [{
id: 124,
name: "Kansas"
}, {
id: 122,
name: "Florida"
}]
}];
$scope.clearSelection = function() {
var values = $scope.available[0].values;
for (var i = 0; i < values.length; i++) {
values[i].checked = false;
}
};
}
HTML:
<body ng-controller="DataMock">
<ul class="list-box">
<li>
<button ng-click="clearSelection()">Clear Selection</button>
</li>
<li ng-repeat="val in available[0].values">
<div class="checkbox">
<label>
<input ng-model="val.checked"
type="checkbox" /> {{val.name}}
</label>
</div>
</li>
</ul>
</body>
Demo on Plunker
The repeat that I used to grab the values based on the id, was the problem area.
<li ng-repeat="val in valuesListValues(tempFilter.fieldId)">
removing that and simple listening and setting a static variable resolved the problem.
$scope.$watch('tempFilter.fieldId', function () {
var fId = $scope.tempFilter.fieldId;
if ($scope.isFieldType(fId, 'valuesList')) {
$scope.valuesListValues = $scope.valuesListValues(fId);
}
}, true);
});
and then in the view:
ng-repeat="value in valuesListValues"

Resources