gpt4 book ai didi

javascript - 卡在使用 RxJS 删除计数器应用程序中的所有内容

转载 作者:行者123 更新时间:2023-11-30 00:09:38 26 4
gpt4 key购买 nike

我正在尝试学习 RxJS observables。在柜台上编写了一个示例应用程序。

  • 应用默认有一个计数器
  • 用户可以添加一个新的计数器,每个计数器都有自己的递增、递减和删除按钮。
  • 用户可以删除计数器
  • 用户可以在页面上看到所有计数器的总数。 (不是计数器的数量,而是所有计数器值的总和)。

    例如:

    • 页面上有两个计数器,其值分别为 2,3。计数器总数应为 2+3 = 5。

    • 如果计数器 1 被移除,计数器总计应为 5-2 = 3

  • 用户可以一次删除所有计数器。

我遇到的问题是每当我点击删除按钮时,它应该从计数器总数中扣除该计数器值。我在删除按钮上使用了一个主题来观察它并解决了。

后来我添加了 removeAll 按钮,我遇到了与以前类似的问题。我无法将计数器总流设置为 0。

我尝试了 .last() 方法,但由于可观察对象未结束,我无法获得计数器总可观察对象的最后状态。

我使用了 merge() 但它没有解决问题。

我解决不了。

我想重新添加一个主题,但是我遇到了两次这个问题,现在我想知道是否有比添加主题更好的解决方案?或者我可能遗漏了什么。

```

// Code goes here
function createCounter(number){
return "<div class='counter' id='counter" + number + "'>" +
'<button id="increment' + number + '">+</button>' +
'<h1 style="display:inline-block; margin: 10px" id="counterValue' + number + '"></h1>' +
'<button id="decrement' + number + '">-</button>' +
'<button id="remove' + number + '">Remove</button>'
"</div>";
}
$(document).ready(function(){
var addCounter$ = Rx.Observable.fromEvent($("#addCounter"), 'click')
.map(()=> 1)
.startWith(0)
.scan((x,y) => x+y);


var countersSubject = new Rx.Subject();
var removeAll$ = Rx.Observable.fromEvent($("#removeAll"), 'click').map(() => 0).startWith(0);
var countersTotal$ = countersSubject.startWith(0).scan((x,y) => x+y).merge(removeAll$);

removeAll$.subscribe(() =>{
$('#counterContainer').empty();
});

countersTotal$.subscribe(total => {
$('#countersTotal').text(total);
});




addCounter$.subscribe(counterNum => {
$('#counterContainer').append(createCounter(counterNum));

var increment$ = Rx.Observable.fromEvent($("#increment" + counterNum), 'click')
.map(() => +1);
var decrement$ = Rx.Observable.fromEvent($("#decrement" + counterNum), 'click')
.map(() => -1);
var action$ = Rx.Observable.merge(increment$, decrement$);

var state$ = action$.startWith(0).scan((prev, now) => prev+now);
var counterSubs = state$.subscribe(val => {
$("#counterValue" + counterNum).text(val);
});

var countersTotalSubs = action$.subscribe(countersSubject);

var remove$ = Rx.Observable.fromEvent($("#remove" + counterNum), 'click');
var removeCounterTotal$ = Rx.Observable.combineLatest(remove$, state$,
(x,y) => -y);
removeCounterTotal$.subscribe(countersSubject);

remove$.subscribe(remove => {
countersTotalSubs.dispose();
counterSubs.dispose();
$('div').remove('#counter' + counterNum);
});

});

});

请看这里。 http://plnkr.co/flJx4LNRiboPmW0GjZtl

如果您需要什么,请告诉我。

谢谢

最佳答案

你是对的,你可以不用Subjects。我会说这是一个相当困难的挑战,要立即开始(所以支持你!),但肯定是可行的。

;tldr 查看更新的 plunkr here

说明

您已经很好地理解了“一切皆流”,所以让我们分解一下。首先,每个计数器都是它自己的组件,它是几个不同流(递增、递减、删除)的合并。因此,让我们从那里开始,然后从那里向外移动。

首先简化您的流以增加、减少删除,因为它也是一种行为:

$('#counterContainer').append(createCounter(counterNum));

//The map operator can take a value which it will map to every value it receives
var inc$ = Rx.Observable.fromEvent($('#increment' + counterNum), 'click').map(+1);
var dec$ = Rx.Observable.fromEvent($('#decrement' + counterNum), 'click').map(-1);
var remove$ = Rx.Observable.fromEvent($('#remove' + counterNum), 'click');

接下来我们使用merge + scan 技术来保持计数器值的运行总和。

return Rx.Observable.merge(inc$, dec$)
.startWith(0)
.scan((prev, now) => prev + now);

但现在我们开始第一个转折,我们知道我们只想获取直到计数器被移除(注意强调),进一步,我们知道通过单击删除我们实际上想从 div 中删除计数器。通过结合这些想法,我们可以添加两种新行为:

return Rx.Observable.merge(inc$, dec$)
.startWith(0)
.scan((prev, now) => prev + now)

//Complete when the remove button is clicked
.takeUntil(remove$)

//When completed remove this counter
.finally(() => $('div').remove('#counter' + counterNum))

//Show the value
.do(val => $('#counterValue' + counterNum).text(val));

为了让所有计数器能够同时运行,我们应该将它们合并在一起,因为我们实际上需要两个值,一个总值和一个增量值,我们将拆分出两个流,一个将在内部用于更新计数器的值和另一个是增量,用于更新总值。

为此,您可以使用 shareshareReplay 以及 using 运算符将所有这些流连接在一起。

//flatMap has an index parameter which can be used here to tally the total
//number of counters "in-flight"
var counters = addCounter$.flatMap((counterNum, idx) => {

$('#counterContainer').append(createCounter(counterNum));
var inc$ = Rx.Observable.fromEvent($('#increment' + counterNum), 'click').map(+1);
var dec$ = Rx.Observable.fromEvent($('#decrement' + counterNum), 'click').map(-1);

//Merges all the events together to describe their logic and then
//shares the resulting Observable
var counter = Rx.Observable.merge(inc$, dec$)
.takeUntil(remove$)
.startWith(0)
.share();

//Creates an Observable that will always emit the last value it
//recieved to all new subscribers
var total = counter
.scan((prev, now) => prev + now, 0)
.shareReplay(1);

return Rx.Observable.using(

//Starts the `total` Observable and updates the counter value
//when a button is pressed
//Ties the subscription's lifetime to that of `counter`
() => total.subscribe(val => $('#counterValue' + counterNum).text(val)),

//Returns the counter Observable
() => counter
)
.finally(() => $('div').remove('#counter' + counterNum))
//When the above Observable completes we will emit one last message
//which will be the total * -1 (subtracting the value from the overall total)
.concat(total.last().map(x => x * -1));
});

现在定义 addCounter$ 的地方:

var addCounter$ = Rx.Observable.fromEvent($("#addCounter"), 'click')

//Map also takes an index parameter which can be leveraged here
.map((_, idx) => idx);

最后我们需要将其组合在一起,拼图的最后一 block 是removeAll 功能。到目前为止,我们所看到的所有内容都可以被认为是此功能的子流,因为全部删除有点像“恢复”状态。我们可以获取 counters 流并将其包装在每次点击 remove all 时重新启动的流中,因为我们的内部 Observables 会自动清理,我们也会神奇地删除他们都在这个过程中。

var removeAll$ = Rx.Observable.fromEvent($("#removeAll"), 'click');

removeAll$
.startWith(0)
.flatMapLatest(() => {
resetTotal();
//Total all the deltas from all of the counters
return adder.scan((acc, val) => acc + val);
})
.subscribe(x => $('#countersTotal').text(x));

仅此而已!请参阅上面我更新的 plunkr 和工作示例(也在下面复制)。

// Code goes here
function createCounter(number){
return "<div class='counter' id='counter" + number + "'>" +
'<button id="increment' + number + '">+</button>' +
'<h1 style="display:inline-block; margin: 10px" id="counterValue' + number + '"></h1>' +
'<button id="decrement' + number + '">-</button>' +
'<button id="remove' + number + '">Remove</button>'
"</div>";
}
$(document).ready(function(){
var addCounter$ = Rx.Observable.fromEvent($("#addCounter"), 'click')
.map((_, idx) => idx);

function resetTotal() {
$('#countersTotal').text(0);
}


var removeAll$ = Rx.Observable.fromEvent($("#removeAll"), 'click');

var adder = addCounter$.flatMap((counterNum, idx) => {

$('#counterContainer').append(createCounter(counterNum));
var inc$ = Rx.Observable.fromEvent($('#increment' + counterNum), 'click').map(+1);
var dec$ = Rx.Observable.fromEvent($('#decrement' + counterNum), 'click').map(-1);
var remove$ = Rx.Observable.fromEvent($('#remove' + counterNum), 'click');

var counter = Rx.Observable.merge(inc$, dec$)
.takeUntil(remove$)
.startWith(0)
.share();

var total = counter
.scan((prev, now) => prev + now, 0)
.shareReplay(1);

var d = total
.subscribe(val => $('#counterValue' + counterNum).text(val));

return Rx.Observable.using(() => d, () => counter)
.finally(() => $('div').remove('#counter' + counterNum))
.concat(total.last().map(x => x * -1));
});


removeAll$
.startWith(0)
.flatMapLatest(() => {
resetTotal();
return adder.scan((acc, val) => acc + val);
})
.subscribe(x => $('#countersTotal').text(x));
});
<!DOCTYPE html>
<html>

<head>
<script data-require="jquery@2.2.0" data-semver="2.2.0" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script data-require="rxjs@4.1.0" data-semver="4.1.0" src="//cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
<script src="script.js"></script>
</head>

<body>
<div id ="app">

<button id="addCounter">Add counter</button>
<button id="removeAll">Remove all</button>
<h1 >Counters Total <span id="countersTotal" ></span></h1>
<div id="counterContainer"></div>
</div>
</body>

</html>

关于javascript - 卡在使用 RxJS 删除计数器应用程序中的所有内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37028892/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com