how to update value from one component to another? - angular7

I am developing a shopping cart module using angular on front-end. I have two component. One is NavBar Component and other is ProductCard Component. I want to update total quantity in NavBar Component when click add to cart button in ProductCard Component.But total quantity is not updating in NavBar Component when click on add to cart on ProductCard Component.Total quantity is only updated when i refresh the page.
I am using BehaviourSubject to communicate between component. but no result.
shopping-cart.services.ts
#Injectable()
export class ShoppingCartService {
qtyInCarts = new BehaviorSubject(this.countTotalQuantity());
getTotalCartQty = this.qtyInCarts.asObservable();
constructor(private globalService: GlobalService,
private http: HttpClient) {}
setTotalCartQty(qty: number) {
this.qtyInCarts.next(qty);
}
countTotalQuantity() {
const cartId = JSON.parse(localStorage.getItem('cartId'));
let totalQty = 0;
if (cartId) {
this.getCarts()
.subscribe(shopping_cart => {
const carts = shopping_cart['carts'];
for (let i = 0; i < carts.length; i++) {
totalQty += carts[i].qty;
}
this.setTotalCartQty(totalQty);
return totalQty;
});
}
return totalQty;
}
}
bs-navbar.component.ts
export class BsNavbarComponent implements OnInit {
totalQtyInCarts: number;
constructor(private shoppingCartService: ShoppingCartService) {}
ngOnInit() {
this.shoppingCartService.getTotalCartQty
.subscribe((qty) => {
this.totalQtyInCarts = qty;
console.log(this.totalQtyInCarts);
});
}
}
product-card.component.ts
constructor(private cartService: ShoppingCartService) {}
ngOnInit() {
this.cartService.getTotalCartQty
.subscribe((qty) => {
this.totalQtyInCarts = qty;
});
}
addToCart() {
this.cartService.addProductToCart(this.product);
this.totalQtyInCarts += 1;
this.cartService.setTotalCartQty(this.totalQtyInCarts);
console.log(this.totalQtyInCarts);
}
I expect the navBar qty will be updated when i click the button add to cart on ProductCardComponent.

Related

How to make dynamic tree view every node independently

I met a problem while I implemented a angular application with material tree view.
thie tree view is dyniamic, that means that you can add child node if you click add button.
the HTML code looks like this
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding>
<button mat-icon-button disabled></button>
<mat-checkbox class="checklist-leaf-node"
[checked]="checklistSelection.isSelected(node)"
(change)="todoLeafItemSelectionToggle(node)">{{node.item}}</mat-checkbox>
</mat-tree-node>
<mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding>
<button mat-icon-button disabled></button>
<mat-form-field>
<input matInput #itemValue placeholder="New item...">
</mat-form-field>
<mat-form-field appearance="legacy">
<input matInput type="text" [formControl]="locationField" [(ngModel)]="node.field" name="node" [matAutocomplete]="auto" placeholder="Field"/>
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let filteredFieldResult of locationFieldResults" [value]="filteredFieldResult">
{{filteredFieldResult}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<button mat-button (click)="saveNode(node, itemValue.value)">Save</button>
</mat-tree-node>
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
<button mat-icon-button matTreeNodeToggle
[attr.aria-label]="'toggle ' + node.filename">
<mat-icon class="mat-icon-rtl-mirror">
{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
<mat-checkbox [checked]="descendantsAllSelected(node)"
[indeterminate]="descendantsPartiallySelected(node)"
(change)="todoItemSelectionToggle(node)">{{node.item}}</mat-checkbox>
<button mat-icon-button (click)="addNewItem(node)"><mat-icon>add</mat-icon></button>
</mat-tree-node>
</mat-tree>
you can see that I have a input field with autocomplet.
and the ts code is as follow:
import {SelectionModel} from '#angular/cdk/collections';
import {FlatTreeControl} from '#angular/cdk/tree';
import {Component, Injectable} from '#angular/core';
import {MatTreeFlatDataSource, MatTreeFlattener} from '#angular/material/tree';
import {BehaviorSubject} from 'rxjs';
import { FormControl } from '#angular/forms';
/**
* Node for to-do item
*/
export class TodoItemNode {
children: TodoItemNode[];
item: string;
}
/** Flat to-do item node with expandable and level information */
export class TodoItemFlatNode {
item: string;
level: number;
expandable: boolean;
}
/**
* The Json object for to-do list data.
*/
const TREE_DATA = {
Groceries: {
'Almond Meal flour': null,
'Organic eggs': null,
'Protein Powder': null,
Fruits: {
Apple: null,
Berries: ['Blueberry', 'Raspberry'],
Orange: null
}
},
Reminders: [
'Cook dinner',
'Read the Material Design spec',
'Upgrade Application to Angular'
]
};
/**
* Checklist database, it can build a tree structured Json object.
* Each node in Json object represents a to-do item or a category.
* If a node is a category, it has children items and new items can be added under the category.
*/
#Injectable()
export class ChecklistDatabase {
dataChange = new BehaviorSubject<TodoItemNode[]>([]);
get data(): TodoItemNode[] { return this.dataChange.value; }
constructor() {
this.initialize();
}
initialize() {
// Build the tree nodes from Json object. The result is a list of `TodoItemNode` with nested
// file node as children.
const data = this.buildFileTree(TREE_DATA, 0);
// Notify the change.
this.dataChange.next(data);
}
/**
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
* The return value is the list of `TodoItemNode`.
*/
buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
const value = obj[key];
const node = new TodoItemNode();
node.item = key;
if (value != null) {
if (typeof value === 'object') {
node.children = this.buildFileTree(value, level + 1);
} else {
node.item = value;
}
}
return accumulator.concat(node);
}, []);
}
/** Add an item to to-do list */
insertItem(parent: TodoItemNode, name: string) {
if (parent.children) {
parent.children.push({item: name} as TodoItemNode);
this.dataChange.next(this.data);
}
}
updateItem(node: TodoItemNode, name: string) {
node.item = name;
this.dataChange.next(this.data);
}
}
/**
* #title Tree with checkboxes
*/
#Component({
selector: 'tree-checklist-example',
templateUrl: 'tree-checklist-example.html',
styleUrls: ['tree-checklist-example.css'],
providers: [ChecklistDatabase]
})
export class TreeChecklistExample {
/** Map from flat node to nested node. This helps us finding the nested node to be modified */
flatNodeMap = new Map<TodoItemFlatNode, TodoItemNode>();
/** Map from nested node to flattened node. This helps us to keep the same object for selection */
nestedNodeMap = new Map<TodoItemNode, TodoItemFlatNode>();
public locationField: FormControl = new FormControl();
public locationFieldResults = ["abc", "asdfa", "asdfasd"]
/** A selected parent node to be inserted */
selectedParent: TodoItemFlatNode | null = null;
/** The new item's name */
newItemName = '';
treeControl: FlatTreeControl<TodoItemFlatNode>;
treeFlattener: MatTreeFlattener<TodoItemNode, TodoItemFlatNode>;
dataSource: MatTreeFlatDataSource<TodoItemNode, TodoItemFlatNode>;
/** The selection for checklist */
checklistSelection = new SelectionModel<TodoItemFlatNode>(true /* multiple */);
constructor(private _database: ChecklistDatabase) {
this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel,
this.isExpandable, this.getChildren);
this.treeControl = new FlatTreeControl<TodoItemFlatNode>(this.getLevel, this.isExpandable);
this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
_database.dataChange.subscribe(data => {
this.dataSource.data = data;
});
this.locationField.valueChanges.subscribe(inputField => {
this.filterField(inputField);
}
);
}
getLevel = (node: TodoItemFlatNode) => node.level;
isExpandable = (node: TodoItemFlatNode) => node.expandable;
getChildren = (node: TodoItemNode): TodoItemNode[] => node.children;
hasChild = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.expandable;
hasNoContent = (_: number, _nodeData: TodoItemFlatNode) => _nodeData.item === '';
/**
* Transformer to convert nested node to flat node. Record the nodes in maps for later use.
*/
transformer = (node: TodoItemNode, level: number) => {
const existingNode = this.nestedNodeMap.get(node);
const flatNode = existingNode && existingNode.item === node.item
? existingNode
: new TodoItemFlatNode();
flatNode.item = node.item;
flatNode.level = level;
flatNode.expandable = !!node.children;
this.flatNodeMap.set(flatNode, node);
this.nestedNodeMap.set(node, flatNode);
return flatNode;
}
/** Whether all the descendants of the node are selected. */
descendantsAllSelected(node: TodoItemFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every(child =>
this.checklistSelection.isSelected(child)
);
return descAllSelected;
}
/** Whether part of the descendants are selected */
descendantsPartiallySelected(node: TodoItemFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const result = descendants.some(child => this.checklistSelection.isSelected(child));
return result && !this.descendantsAllSelected(node);
}
/** Toggle the to-do item selection. Select/deselect all the descendants node */
todoItemSelectionToggle(node: TodoItemFlatNode): void {
this.checklistSelection.toggle(node);
const descendants = this.treeControl.getDescendants(node);
this.checklistSelection.isSelected(node)
? this.checklistSelection.select(...descendants)
: this.checklistSelection.deselect(...descendants);
// Force update for the parent
descendants.every(child =>
this.checklistSelection.isSelected(child)
);
this.checkAllParentsSelection(node);
}
/** Toggle a leaf to-do item selection. Check all the parents to see if they changed */
todoLeafItemSelectionToggle(node: TodoItemFlatNode): void {
this.checklistSelection.toggle(node);
this.checkAllParentsSelection(node);
}
/* Checks all the parents when a leaf node is selected/unselected */
checkAllParentsSelection(node: TodoItemFlatNode): void {
let parent: TodoItemFlatNode | null = this.getParentNode(node);
while (parent) {
this.checkRootNodeSelection(parent);
parent = this.getParentNode(parent);
}
}
/** Check root node checked state and change it accordingly */
checkRootNodeSelection(node: TodoItemFlatNode): void {
const nodeSelected = this.checklistSelection.isSelected(node);
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every(child =>
this.checklistSelection.isSelected(child)
);
if (nodeSelected && !descAllSelected) {
this.checklistSelection.deselect(node);
} else if (!nodeSelected && descAllSelected) {
this.checklistSelection.select(node);
}
}
/* Get the parent node of a node */
getParentNode(node: TodoItemFlatNode): TodoItemFlatNode | null {
const currentLevel = this.getLevel(node);
if (currentLevel < 1) {
return null;
}
const startIndex = this.treeControl.dataNodes.indexOf(node) - 1;
for (let i = startIndex; i >= 0; i--) {
const currentNode = this.treeControl.dataNodes[i];
if (this.getLevel(currentNode) < currentLevel) {
return currentNode;
}
}
return null;
}
/** Select the category so we can insert the new item. */
addNewItem(node: TodoItemFlatNode) {
const parentNode = this.flatNodeMap.get(node);
this._database.insertItem(parentNode!, '');
this.treeControl.expand(node);
}
/** Save the node to database */
saveNode(node: TodoItemFlatNode, itemValue: string) {
const nestedNode = this.flatNodeMap.get(node);
this._database.updateItem(nestedNode!, itemValue);
}
private filterField(value: string): string[] {
if (value) {
this.locationFieldResults = this.locationFieldResults.filter((searchFieldResult) => {
return searchFieldResult.indexOf(value) !== -1;
});
} else {
this.locationFieldResults = this.locationFieldResults;
}
return this.locationFieldResults;
}
}
the problem is: If I tipped some content in input field, by adding new child node, the tipped content in old input field disappeared.
any solutions are expected.
this is example from angular material, you can copy the html and ts file into it,
https://stackblitz.com/angular/nnxeaxmrdob?file=src%2Fapp%2Ftree-checklist-example.ts

How to save the record mutipule table using Asp.net MVC Json

i am creating simple sales system for my final year project. i am creating a sales Form. attached the screen shot image below how the form look like.
after sales completed i need to save the data into multiple table along with the lastinsert id. if i click print invoice button. i have a tables in the database sales,sales product i shown the shot shotimage below.i don't how to save records into multipule table with lastinsert id.
enter image description here
Sales Table
id date subtotal
Sales_Product Table
id sales_id product_id price qty total
Code which i tried
jQuery
function addProject() {
var table_data = [];
$('#product_list tbody tr').each(function (row, tr) {
var sub = {
//these records i am going to add into sales table
'barcode': $(tr).find('td:eq(1)').text(),
'pname': $(tr).find('td:eq(2)').text(),
'pro_price': $(tr).find('td:eq(3)').text(),
'qty': $(tr).find('td:eq(4)').text(),
'total_cost': $(tr).find('td:eq(5)').text(),
};
table_data.push(sub);
});
//these records i am going to add into sales
var total = $("#total").val();
$.ajax({
type: 'POST',
url: '/product/Save',
dataType: 'JSON',
data: {
total: $('#total').val(), data: table_data
},
success: function (data) {
console.log(_data);
var msg;
if (isNew) {
msg = "Sales Completed";
}
last_id = data.last_id
window.location.href = "print.php?last_id=" + last_id;
$.alert({
title: 'Success!',
content: msg,
type: 'green',
boxWidth: '400px',
theme: 'light',
useBootstrap: false,
autoClose: 'ok|2000'
});
isNew = true;
},
error: function (xhr, status, error) {
alert(xhr);
}
});
}
Controller
[HttpPost]
public ActionResult Save(sale s)
{
bool status = false;
if (ModelState.IsValid)
{
using (saleEntities3 dc = new saleEntities3())
{
//Sales table
var v = dc.sales.Where(a => a.id == s.id).FirstOrDefault();
dc.sales.Add(v);
dc.SaveChanges();
v.id = s.id; // lastinsertid
//how to add into lastinsertid as a sales product table as a sales_id colum
//Sales product table i don't how to add
status = true;
}
}
return new JsonResult { Data = new { status = status } };
}
saleEntities3
public partial class saleEntities3 : DbContext
{
public saleEntities3()
: base("name=saleEntities3")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<product> products { get; set; }
public virtual DbSet<sale> sales { get; set; }
public virtual DbSet<sales_product> sales_product { get; set; }
}
}
To save in the Sales_Product table you need to save with the id of the saved sales according to your table structure
[HttpPost]
public ActionResult Save(sale s)
{
bool status = false;
if (ModelState.IsValid)
{
using (saleEntities3 dc = new saleEntities3())
{
//Sales table
var v = dc.sales.Where(a => a.id == s.id).FirstOrDefault();
dc.sales.Add(v);
dc.SaveChanges();
dc.sales_product.Add(new sales_product{
sales_id = s.id,
product_id = s.barcode, //I believe this is the product id
price = s.pro_price,
quantity = s.qty,
total = s.total_cost
});
dc.SaveChanges();
status = true;
}
}
return new JsonResult { Data = new { status = status } };
}

list is updated but values are not reflect in the HTML in angular7?

I have created a website in angular7 in which we have fire the function from 1 component to another component function is fire properly but value are not reflect in the html below we have shared updated fiddle:
//Component1.
import { Component, OnInit } from '#angular/core';
import { Router,NavigationExtras } from "#angular/router";
import {UserService} from "../../service/user.service";
import { FormGroup, FormBuilder, FormControl, FormGroupDirective, NgForm, Validators } from '#angular/forms';
import { DashboardComponent } from '../../dashboard/dashboard.component';
declare var $: any;
#Component({
selector: 'app-innerheader',
templateUrl: './innerheader.component.html',
styleUrls: ['./innerheader.component.css'],
providers : [DashboardComponent]
})
export class InnerheaderComponent implements OnInit {
//declare global varible here....
private loggedUserObject : any = {};
private userImageUrl : any;
constructor(
private router : Router,
private service : UserService,
private formBuilder: FormBuilder,
private dashboard : DashboardComponent) {
}
ngOnInit() {
}
//unblockUser for unblock user
unblockUser(user : any) {
//pass the dataparam in the backend....
var data = {
"user_id" : this.loggedUserObject.user_id,
"block_user_id" : this.selectedUser.id
}
//here we will set url via key wise....
var url = '/api/un-block-user-profile';
//saveDetail function for save data in db...
this.service.saveDetail(data, url).subscribe( (data : any) => {
if(data.status) {
$('#BlockedListModal').on('hidden.bs.modal', function () {
this.dashboard.getUserDetail();
})
//her we will remove user from the updated list of block user...
var index = this.blockUserlist.findIndex(x=>x.id == this.selectedUser.id);
if(index != -1) {
this.blockUserlist.splice(index, 1);
}
//remoive card after accept
this.currentElement.remove();
this.service.successAlert(data.message);
}
});
}
}
//header compeonet 2: here we will fire function from another compeonet
getDashboardDetail() {
//hold user info
var data = {
"user_id" : this.currentUserObject.user_id
}
var url = '/api/time-line-list';
//createUser function for create user in DB
this.service.saveDetail(data, url).subscribe( (data : any) => {
if(data.status) {
if(data.result.article_info.length >0) {
if(this.dashboardArticleList.length == 0) {
this.dashboardArticleList = data.result.article_info;
} else {
this.dashboardArticleList = this.dashboardArticleList.concat(data.result.article_info);
}
}
}
});
}
//In the 2nd Component we have show listing inside model.i have reload the list after close model. please check and tell me what the wrong in my code?

Bloc counter for shopping cart

I am trying to build a shopping cart using the bloc pattern as this my first app in flutter as well as using bloc. my problem is that I am trying to get the stream of an int each time the user add the product to the cart. but it seems that I am using the sink and stream wrong but I don't know exactly where
ItemCounterBloc
final _itemCounterSubject = BehaviorSubject<int>(seedValue: 0);
final _cartItemsController = StreamController<List<CartItem>>();
int count = 0;
ItemCounterBloc(Item item){
_cartItemsController.stream
.map((list) => list.any((cartItem)=> cartItem.item == item))
.listen((increment){
count += 1;
_itemCounterSubject.add(count);
});
}
Sink<List<CartItem>> get cartItems => _cartItemsController.sink;
ValueObservable<int> get isInCart => _itemCounterSubject.stream.distinct().shareValue(seedValue: 0);
void dispose(){
_cartItemsController.close();
_itemCounterSubject.close();
}
}
Counter
StreamBuilder<int>(
stream: _bloc.isInCart,
initialData:0,
builder: (context, snapshot) => Text('${snapshot.data}')
Also I have another bloc for adding items to the cart.
There is a full example on how to build a shopping cart system.
Including the following parts :
Adding / Removing items from the cart
AppBar counter with the amount of items in the cart
Shopping Cart BLOC
https://github.com/Ephenodrom/FlutterAdvancedExamples/tree/master/lib/examples/shoppingCart
This is how your BLOC could look :
class ShoppingCartBloc implements BlocBase {
static const String TAG = "ShoppingCartBloc";
ShoppingCart cart = ShoppingCart();
/// Sinks
Sink<Product> get addition => itemAdditionController.sink;
final itemAdditionController = StreamController<Product>();
Sink<Product> get substraction => itemSubtractionController.sink;
final itemSubtractionController = StreamController<Product>();
/// Streams
Stream<ShoppingCart> get cartStream => _cart.stream;
final _cart = BehaviorSubject<ShoppingCart>();
ShoppingCartBloc() {
itemAdditionController.stream.listen(handleItemAdd);
itemSubtractionController.stream.listen(handleItemRem);
}
///
/// Logic for product added to shopping cart.
///
void handleItemAdd(Product item) {
Logger(TAG).info("Add product to the shopping cart");
cart.addProduct(item);
cart.calculate();
_cart.add(cart);
return;
}
///
/// Logic for product removed from shopping cart.
///
void handleItemRem(Product item) {
Logger(TAG).info("Remove product from the shopping cart");
cart.remProduct(item);
cart.calculate();
_cart.add(cart);
return;
}
///
/// Clears the shopping cart
///
void clearCart() {
cart.clear();
}
#override
void dispose() {
itemAdditionController.close();
itemSubtractionController.close();
}
}
class ShoppingCart {
List<Product> products = [];
double priceNet;
double priceGross;
double vatAmount;
void addProduct(Product p) {
products.add(p);
}
void remProduct(Product p) {
products.remove(p);
}
void calculate() {
priceNet = 0;
priceGross = 0;
vatAmount = 0;
products.forEach((p) {
priceNet += p.priceNet;
priceGross += p.priceGross;
vatAmount += p.vatAmount;
});
}
void clear() {
products = [];
priceNet = 0;
priceGross = 0;
vatAmount = 0;
}
}
class Product {
final String name;
final double priceNet;
final double priceGross;
final double vatAmount;
final double tax;
Product(
{this.name, this.priceNet, this.priceGross, this.vatAmount, this.tax});
}

Angular 2 Custom Form Component: Provide a markTouched method

I have a custom form component that implements ControlValueAccessor. This component has an internal property touched.
export class BmInputComponent implements ControlValueAccessor, Validator {
private onTouchedCallback: () => {};
private touched: boolean = false;
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
onBlur() {
this.touched = true;
this.onTouchedCallback();
}
}
I need to implement a method like
markTouched() {
this.touched = true;
}
That could be called by the user of the component when markAsTouched is executed in the formControl: customInputControl.markAsTouched()
I cannot find an angular-way to do this.
#Edit:
Tried to inject the NgControl:
#Component({
selector: 'bm-input',
templateUrl: './bm-input.component.html',
encapsulation: ViewEncapsulation.None,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => BmInputComponent),
multi: true
}
]
})
export class BmInputComponent implements ControlValueAccessor, Validator {
private onTouchedCallback: () => {};
private touched: boolean = false;
constructor(#Self() #Optional() public _formControl: NgControl) {
this._viewDate = new Date();
if (this._formControl) {
this._formControl.valueAccessor = this;
this._formControl.statusChanges.subscribe(this.markTouched);
}
}
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
onBlur() {
this.touched = true;
this.onTouchedCallback();
}
markTouched() {
if(this._formControl.touched)
this.touched = true;
}
}
But I am getting Cannot instantiate cyclic dependency! NgControl when the component is invoked with a formControl.
Have you tried #SkipSelf() instead of #Self()?
You could try this:
constructor(private injector: Injector) {
}
ngDoCheck() {
let ngControl = this.injector.get(NgControl);
if (! ngControl.control) {
return;
}
this.touched = ngControl.control.touched;
}
The circular dependency is caused by having both the NG_VALUE_ACCESSOR in your #Component(...) providers, and injecting NgControl in the constructor. These are mutually exclusive.
See the example in the NG material documentation here: https://material.angular.io/guide/creating-a-custom-form-field-control#ngcontrol

Resources